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 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 "xpm-ximage.h"
89 # include "textclient.h"
95 #ifndef HAVE_JWZGLES /* No SPHERE_MAP on iPhone */
100 # include "../images/chromesphere.xpm"
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 = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
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,
335 GL_UNSIGNED_INT_8_8_8_8_REV,
340 check_gl_error("texture");
342 glEnable(GL_TEXTURE_GEN_S);
343 glEnable(GL_TEXTURE_GEN_T);
344 glEnable(GL_TEXTURE_2D);
347 #endif /* HAVE_TEXTURE */
350 static int unit_gear (ModeInfo *, GLfloat color[4]);
351 static int draw_ground (ModeInfo *, GLfloat color[4]);
352 static void init_walker (ModeInfo *, walker *);
355 parse_color (ModeInfo *mi, char *key, GLfloat color[4])
358 char *string = get_string_resource (mi->dpy, key, "RobotColor");
359 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
361 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
366 color[0] = xcolor.red / 65536.0;
367 color[1] = xcolor.green / 65536.0;
368 color[2] = xcolor.blue / 65536.0;
374 init_robot (ModeInfo *mi)
376 robot_configuration *bp;
377 int wire = MI_IS_WIREFRAME(mi);
381 bp = &bps[MI_SCREEN(mi)];
383 bp->glx_context = init_GL(mi);
385 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
387 glShadeModel(GL_SMOOTH);
389 glEnable(GL_DEPTH_TEST);
390 glEnable(GL_NORMALIZE);
391 glEnable(GL_CULL_FACE);
395 GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
396 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
397 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
398 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
399 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
401 glEnable(GL_LIGHTING);
403 glEnable(GL_DEPTH_TEST);
404 glEnable(GL_CULL_FACE);
406 glLightfv(GL_LIGHT0, GL_POSITION, pos);
407 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
408 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
409 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
411 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
415 if (!wire && do_texture)
419 bp->user_trackball = gltrackball_init (False);
421 bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
422 for (i = 0; i < countof(all_objs); i++)
423 bp->dlists[i] = glGenLists (1);
425 for (i = 0; i < countof(all_objs); i++)
427 const struct gllist *gll = *all_objs[i];
429 GLfloat spec1[4] = {1.00, 1.00, 1.00, 1.0};
430 GLfloat spec2[4] = {0.40, 0.40, 0.70, 1.0};
434 glNewList (bp->dlists[i], GL_COMPILE);
436 glMatrixMode(GL_MODELVIEW);
438 glMatrixMode(GL_TEXTURE);
440 glMatrixMode(GL_MODELVIEW);
442 glRotatef (-90, 1, 0, 0);
443 glRotatef (180, 0, 0, 1);
446 glBindTexture (GL_TEXTURE_2D, 0);
456 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
457 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
458 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
469 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
470 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
471 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
511 key = "gearboxColor";
515 case ROBOT_WIREFRAME:
529 parse_color (mi, key, bp->component_colors[i]);
531 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
532 glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
537 robot_dome = (struct gllist *) calloc (1, sizeof(*robot_dome));
538 robot_dome->points = unit_dome (32, 32, MI_IS_WIREFRAME(mi));
542 robot_gear = (struct gllist *) calloc (1, sizeof(*robot_gear));
543 robot_gear->points = unit_gear (mi, bp->component_colors[i]);
547 ground = (struct gllist *) calloc (1, sizeof(*ground));
548 ground->points = draw_ground (mi, bp->component_colors[i]);
550 case ROBOT_WIREFRAME:
552 renderList (gll, True);
555 renderList (gll, wire);
556 /* glColor3f (1, 1, 1); renderListNormals (gll, 100, True); */
557 /* glColor3f (1, 1, 0); renderListNormals (gll, 100, False); */
561 glMatrixMode(GL_TEXTURE);
563 glMatrixMode(GL_MODELVIEW);
570 if (debug_p) MI_COUNT(mi) = 1;
573 bp->nwalkers = MI_COUNT(mi);
574 bp->walkers = (walker *) calloc (bp->nwalkers, sizeof (walker));
576 for (i = 0; i < bp->nwalkers; i++)
577 init_walker (mi, &bp->walkers[i]);
579 /* Since #0 is the one we track, make sure it doesn't walk too straight.
581 bp->walkers[0].balance *= 1.5;
584 bp->font_data = load_texture_font (mi->dpy, "labelFont");
585 bp->max_lines = get_integer_resource (mi->dpy, "textLines", "TextLines");
586 bp->tc = textclient_open (MI_DISPLAY (mi));
588 parse_color (mi, "textColor", bp->text_color);
589 parse_color (mi, "textBackground", bp->text_bg);
590 parse_color (mi, "textBorderColor", bp->text_bd);
592 reshape_robot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
594 # endif /* WORDBUBBLES */
599 /* Let's tilt the floor a little. */
600 gltrackball_reset (bp->user_trackball,
607 draw_component (ModeInfo *mi, int i)
609 robot_configuration *bp = &bps[MI_SCREEN(mi)];
610 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
611 bp->component_colors[i]);
612 glCallList (bp->dlists[i]);
613 return (*all_objs[i])->points / 3;
617 draw_transparent_component (ModeInfo *mi, int i, GLfloat alpha)
619 robot_configuration *bp = &bps[MI_SCREEN(mi)];
620 int wire = MI_IS_WIREFRAME(mi);
623 if (alpha < 0) return 0;
624 if (alpha > 1) alpha = 1;
625 bp->component_colors[i][3] = alpha;
627 if (wire || alpha >= 1)
628 return draw_component (mi, i);
630 /* Draw into the depth buffer but not the frame buffer */
631 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
632 count += draw_component (mi, i);
634 /* Now draw into the frame buffer only where there's already depth */
635 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
636 glDepthFunc (GL_EQUAL);
638 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
640 count += draw_component (mi, i);
641 glDepthFunc (GL_LESS);
642 glDisable (GL_BLEND);
649 draw_arm_half (ModeInfo *mi, walker *f)
653 count += draw_transparent_component (mi, ROBOT_ARM, f->body_transparency);
659 draw_hand_half (ModeInfo *mi, walker *f)
663 count += draw_transparent_component (mi, ROBOT_HAND, f->body_transparency);
669 /* rotation of arm: 0-360.
670 openness of fingers: 0.0 - 1.0.
673 draw_arm (ModeInfo *mi, walker *f, GLfloat left_p, GLfloat rot, GLfloat open)
676 GLfloat arm_x = 4766; /* distance from origin to arm axis */
677 GLfloat arm_y = 12212;
679 open *= 5.5; /* scale of finger range */
682 if (debug_p) rot = 0;
688 glTranslatef (0, 0, arm_x * 2);
690 glTranslatef (0, arm_y, -arm_x); /* move to origin */
691 glRotatef (rot, 1, 0, 0);
692 glTranslatef (0, -arm_y, arm_x); /* move back */
695 count += draw_arm_half (mi, f);
698 glTranslatef (0, -arm_y * 2, 0);
700 count += draw_arm_half (mi, f);
703 glTranslatef (0, 0, -arm_x * 2);
706 count += draw_arm_half (mi, f);
709 glTranslatef (0, -arm_y * 2, 0);
711 count += draw_arm_half (mi, f);
714 glTranslatef (0, 0, open);
716 count += draw_hand_half (mi, f);
718 glTranslatef (0, 0, -open);
720 glTranslatef (0, 0, arm_x * 2);
722 glTranslatef (0, 0, open);
723 count += draw_hand_half (mi, f);
731 draw_body_half (ModeInfo *mi, walker *f, Bool inside_p)
734 int which = (inside_p ? ROBOT_BODY_2 : ROBOT_BODY_1);
736 count += draw_transparent_component (mi, which, f->body_transparency);
743 draw_body (ModeInfo *mi, walker *f, Bool inside_p)
749 count += draw_body_half (mi, f, inside_p);
753 count += draw_body_half (mi, f, inside_p);
762 draw_gearbox_half (ModeInfo *mi)
766 count += draw_component (mi, ROBOT_GEARBOX);
773 draw_gearbox (ModeInfo *mi)
779 count += draw_gearbox_half (mi);
783 count += draw_gearbox_half (mi);
791 unit_gear (ModeInfo *mi, GLfloat color[4])
793 int wire = MI_IS_WIREFRAME(mi);
801 g->thickness2 = g->thickness * 0.5;
802 g->thickness3 = g->thickness;
803 g->inner_r = g->r * 0.7;
804 g->inner_r2 = g->r * 0.4;
805 g->inner_r3 = g->r * 0.1;
806 g->size = INVOLUTE_LARGE;
808 g->color[0] = g->color2[0] = color[0];
809 g->color[1] = g->color2[1] = color[1];
810 g->color[2] = g->color2[2] = color[2];
811 g->color[3] = g->color2[3] = color[3];
813 return draw_involute_gear (g, wire);
818 draw_gear (ModeInfo *mi)
823 count += draw_component (mi, ROBOT_GEAR);
829 draw_crank (ModeInfo *mi, walker *f, GLfloat rot)
832 GLfloat origin = 12210.0;
838 glTranslatef (0, origin, 0); /* position at origin */
839 glRotatef (rot, 0, 0, 1);
842 glRotatef (90, 1, 0, 0);
843 count += draw_gear (mi);
846 glTranslatef (0, -origin, 0); /* move back */
849 count += draw_component (mi, ROBOT_CRANK);
858 draw_rotator_half (ModeInfo *mi)
862 count += draw_component (mi, ROBOT_ROTATOR);
869 draw_rotator (ModeInfo *mi, walker *f, GLfloat rot)
872 GLfloat origin = 10093.0;
876 glTranslatef (0, origin, 0); /* position at origin */
877 glRotatef (rot, 0, 0, 1);
880 glRotatef (90, 1, 0, 0);
881 count += draw_gear (mi);
887 glDisable(GL_LIGHTING);
889 glVertex3f(0, 0, -3000);
890 glVertex3f(0, 0, 3000);
892 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
896 glTranslatef (0, -origin, 0); /* move back */
899 count += draw_rotator_half (mi);
903 glRotatef (180, 0, 0, 1);
904 glTranslatef (0, -origin * 2, 0); /* move back */
905 count += draw_rotator_half (mi);
913 draw_leg_half (ModeInfo *mi)
917 count += draw_component (mi, ROBOT_LEG);
924 draw_wireframe (ModeInfo *mi, walker *f)
926 int wire = MI_IS_WIREFRAME(mi);
928 GLfloat alpha = 0.6 - f->body_transparency;
929 if (alpha < 0) return 0;
931 if (!wire) glDisable (GL_LIGHTING);
933 count += draw_transparent_component (mi, ROBOT_WIREFRAME, alpha);
935 if (!wire) glEnable (GL_LIGHTING);
941 draw_leg (ModeInfo *mi, GLfloat rot, Bool left_p)
945 GLfloat leg_distance = 9401; /* distance from ground to leg axis */
946 GLfloat rot_distance = 10110; /* distance from ground to rotator axis */
947 GLfloat pin_distance = 14541; /* distance from ground to stop pin */
948 GLfloat orbit_r = rot_distance - leg_distance; /* radius of rotation */
950 /* Actually it's the bottom of the pin minus its diameter, or something. */
956 glRotatef (180, 0, 1, 0);
958 if (!left_p) rot = -(rot + 180);
962 x = orbit_r * cos (-rot * M_PI / 180);
963 y = orbit_r * sin (-rot * M_PI / 180);
966 /* Rotate the leg by angle B of the right A
967 triangle ABC, where: /:
969 A = position of stop pin / :
970 D = position of rotator wheel's axis , - ~ ~ ~ - ,
971 C = D + y , ' / : ' ,
972 B = D + xy (leg's axis) , / : ,
975 H = dist(A,B) , / D ,
976 O = dist(A,C) , / : ,
977 sin(th) = O/H , / : ,
978 th = asin(O/H) B ~ ~ ~ ~ ~ C .
982 GLfloat Ay = pin_distance - leg_distance;
983 GLfloat Cx = 0, Cy = y;
985 GLfloat dBC = Cx - Bx;
986 GLfloat dAC = Cy - Ay;
987 GLfloat dAB = sqrt (dBC*dBC + dAC*dAC);
988 GLfloat th = asin (dAC / dAB);
989 rot = th / (M_PI / 180.0);
991 if (dBC > 0) rot = 360-rot;
994 glTranslatef (0, orbit_r, 0); /* position on rotator axis */
995 glTranslatef (x, y, 0);
997 glTranslatef (0, leg_distance, 0); /* position on leg axis */
998 glRotatef(rot, 0, 0, 1);
999 glTranslatef (0, -leg_distance, 0); /* move back */
1001 glFrontFace(GL_CCW);
1002 count += draw_leg_half (mi);
1004 glScalef (-1, 1, 1);
1006 count += draw_leg_half (mi);
1014 draw_dome (ModeInfo *mi, walker *f)
1016 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1017 int wire = MI_IS_WIREFRAME(mi);
1018 int which = ROBOT_DOME;
1021 GLfloat trans = f->body_transparency;
1023 GLfloat dome_y = 15290;
1025 if (trans < 0) trans = 0;
1026 if (trans > max) trans = max;
1028 if (!wire) glEnable (GL_BLEND);
1031 glTranslatef (0, dome_y, 0);
1032 glScalef (100, 100, 100);
1033 glRotatef (90, 1, 0, 0);
1034 glTranslatef (0.35, 0, 0);
1036 glFrontFace(GL_CCW);
1037 bp->component_colors[which][3] = trans;
1038 count += draw_component (mi, which);
1041 if (!wire) glDisable (GL_BLEND);
1047 /* Is this robot overlapping any other?
1050 collision_p (ModeInfo *mi, walker *w, GLfloat extra_space)
1052 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1054 if (MI_COUNT(mi) <= 1) return False;
1056 for (i = 0; i < MI_COUNT(mi); i++)
1058 walker *w2 = &bp->walkers[i];
1059 GLfloat min = 0.75 + extra_space;
1061 if (w == w2) continue;
1064 d = (dx*dx + dy*dy);
1065 if (d <= min*min) return True;
1071 /* I couldn't figure out a straightforward trig solution to the
1072 forward/backward tilting that happens as weight passes from one
1073 foot to another, so I just built a tool to eyeball it manually.
1074 { vertical_translation, rotation, forward_translation }
1076 static const struct { GLfloat up, rot, fwd; } wobble_profile[360] = {
1077 { 0.0000, 0.00, 0.0000 }, /* 0 */
1078 { 0.0000, -0.01, 0.0025 },
1079 { 0.0000, -0.25, 0.0040 },
1080 { 0.0000, -0.41, 0.0060 },
1081 { 0.0000, -0.62, 0.0080 },
1082 { 0.0000, -0.80, 0.0095 },
1083 { 0.0000, -0.90, 0.0120 },
1084 { 0.0000, -1.10, 0.0135 },
1085 { 0.0000, -1.25, 0.0150 },
1086 { 0.0000, -1.40, 0.0175 },
1087 { 0.0000, -1.50, 0.0195 }, /* 10 */
1088 { 0.0000, -1.70, 0.0215 },
1089 { 0.0000, -1.80, 0.0230 },
1090 { 0.0000, -2.00, 0.0250 },
1091 { 0.0000, -2.10, 0.0270 },
1092 { -0.0005, -2.30, 0.0290 },
1093 { -0.0005, -2.50, 0.0305 },
1094 { -0.0005, -2.60, 0.0330 },
1095 { -0.0005, -2.70, 0.0330 },
1096 { -0.0005, -2.90, 0.0350 },
1097 { -0.0005, -3.00, 0.0365 }, /* 20 */
1098 { -0.0010, -3.20, 0.0380 },
1099 { -0.0005, -3.30, 0.0400 },
1100 { -0.0010, -3.50, 0.0420 },
1101 { -0.0010, -3.70, 0.0440 },
1102 { -0.0010, -3.80, 0.0460 },
1103 { -0.0010, -3.90, 0.0470 },
1104 { -0.0015, -4.10, 0.0500 },
1105 { -0.0015, -4.20, 0.0515 },
1106 { -0.0015, -4.40, 0.0535 },
1107 { -0.0015, -4.50, 0.0550 }, /* 30 */
1108 { -0.0015, -4.60, 0.0565 },
1109 { -0.0020, -4.80, 0.0585 },
1110 { -0.0020, -5.00, 0.0600 },
1111 { -0.0020, -5.10, 0.0620 },
1112 { -0.0025, -5.20, 0.0635 },
1113 { -0.0025, -5.40, 0.0655 },
1114 { -0.0025, -5.50, 0.0675 },
1115 { -0.0025, -5.60, 0.0690 },
1116 { -0.0030, -5.80, 0.0710 },
1117 { -0.0030, -5.90, 0.0720 }, /* 40 */
1118 { -0.0030, -6.00, 0.0740 },
1119 { -0.0035, -6.10, 0.0760 },
1120 { -0.0035, -6.30, 0.0790 },
1121 { -0.0040, -6.40, 0.0805 },
1122 { -0.0040, -6.50, 0.0820 },
1123 { -0.0040, -6.60, 0.0835 },
1124 { -0.0045, -6.80, 0.0855 },
1125 { -0.0045, -6.90, 0.0870 },
1126 { -0.0050, -7.00, 0.0885 },
1127 { -0.0050, -7.20, 0.0900 }, /* 50 */
1128 { -0.0050, -7.20, 0.0915 },
1129 { -0.0055, -7.40, 0.0930 },
1130 { -0.0055, -7.50, 0.0945 },
1131 { -0.0060, -7.60, 0.0960 },
1132 { -0.0060, -7.70, 0.0970 },
1133 { -0.0065, -7.80, 0.0985 },
1134 { -0.0060, -7.70, 0.0995 },
1135 { -0.0060, -7.60, 0.1010 },
1136 { -0.0060, -7.50, 0.1020 },
1137 { -0.0055, -7.30, 0.1030 }, /* 60 */
1138 { -0.0050, -7.10, 0.1040 },
1139 { -0.0050, -6.90, 0.1050 },
1140 { -0.0045, -6.80, 0.1065 },
1141 { -0.0045, -6.50, 0.1075 },
1142 { -0.0040, -6.40, 0.1085 },
1143 { -0.0040, -6.20, 0.1095 },
1144 { -0.0040, -6.00, 0.1105 },
1145 { -0.0035, -5.80, 0.1115 },
1146 { -0.0030, -5.50, 0.1125 },
1147 { -0.0030, -5.40, 0.1135 }, /* 70 */
1148 { -0.0030, -5.10, 0.1145 },
1149 { -0.0030, -4.90, 0.1150 },
1150 { -0.0025, -4.70, 0.1160 },
1151 { -0.0025, -4.40, 0.1165 },
1152 { -0.0025, -4.20, 0.1175 },
1153 { -0.0020, -3.90, 0.1180 },
1154 { -0.0020, -3.70, 0.1185 },
1155 { -0.0020, -3.40, 0.1190 },
1156 { -0.0020, -3.10, 0.1195 },
1157 { -0.0020, -2.90, 0.1200 }, /* 80 */
1158 { -0.0015, -2.60, 0.1200 },
1159 { -0.0015, -2.30, 0.1205 },
1160 { -0.0015, -2.00, 0.1210 },
1161 { -0.0015, -1.80, 0.1215 },
1162 { -0.0015, -1.50, 0.1215 },
1163 { -0.0015, -1.20, 0.1215 },
1164 { -0.0015, -0.90, 0.1215 },
1165 { -0.0015, -0.60, 0.1215 },
1166 { -0.0015, -0.30, 0.1215 },
1167 { -0.0010, 0.00, 0.1215 }, /* 90 */
1168 { -0.0010, 0.30, 0.1215 },
1169 { -0.0010, 0.60, 0.1215 },
1170 { -0.0010, 0.90, 0.1215 },
1171 { -0.0010, 1.20, 0.1215 },
1172 { -0.0015, 1.40, 0.1215 },
1173 { -0.0015, 1.70, 0.1215 },
1174 { -0.0015, 2.00, 0.1215 },
1175 { -0.0015, 2.30, 0.1215 },
1176 { -0.0015, 2.60, 0.1215 },
1177 { -0.0015, 2.80, 0.1220 }, /* 100 */
1178 { -0.0020, 3.10, 0.1225 },
1179 { -0.0020, 3.30, 0.1230 },
1180 { -0.0020, 3.60, 0.1235 },
1181 { -0.0020, 3.90, 0.1240 },
1182 { -0.0025, 4.10, 0.1245 },
1183 { -0.0025, 4.40, 0.1250 },
1184 { -0.0025, 4.60, 0.1260 },
1185 { -0.0025, 4.90, 0.1265 },
1186 { -0.0030, 5.10, 0.1275 },
1187 { -0.0030, 5.30, 0.1285 }, /* 110 */
1188 { -0.0035, 5.60, 0.1290 },
1189 { -0.0035, 5.80, 0.1300 },
1190 { -0.0035, 6.00, 0.1310 },
1191 { -0.0040, 6.20, 0.1325 },
1192 { -0.0040, 6.40, 0.1335 },
1193 { -0.0045, 6.60, 0.1345 },
1194 { -0.0045, 6.70, 0.1355 },
1195 { -0.0050, 6.90, 0.1365 },
1196 { -0.0050, 7.10, 0.1375 },
1197 { -0.0055, 7.30, 0.1390 }, /* 120 */
1198 { -0.0055, 7.40, 0.1400 },
1199 { -0.0060, 7.50, 0.1415 },
1200 { -0.0065, 8.00, 0.1425 },
1201 { -0.0065, 7.80, 0.1440 },
1202 { -0.0060, 7.80, 0.1455 },
1203 { -0.0060, 7.60, 0.1470 },
1204 { -0.0055, 7.50, 0.1485 },
1205 { -0.0055, 7.40, 0.1500 },
1206 { -0.0050, 7.30, 0.1515 },
1207 { -0.0050, 7.20, 0.1530 }, /* 130 */
1208 { -0.0050, 7.00, 0.1545 },
1209 { -0.0045, 6.90, 0.1560 },
1210 { -0.0045, 6.80, 0.1575 },
1211 { -0.0040, 6.70, 0.1590 },
1212 { -0.0040, 6.50, 0.1605 },
1213 { -0.0040, 6.40, 0.1625 },
1214 { -0.0035, 6.30, 0.1640 },
1215 { -0.0035, 6.10, 0.1655 },
1216 { -0.0030, 6.10, 0.1670 },
1217 { -0.0030, 5.90, 0.1690 }, /* 140 */
1218 { -0.0030, 5.70, 0.1705 },
1219 { -0.0025, 5.70, 0.1720 },
1220 { -0.0025, 5.50, 0.1740 },
1221 { -0.0025, 5.40, 0.1755 },
1222 { -0.0025, 5.20, 0.1775 },
1223 { -0.0020, 5.10, 0.1790 },
1224 { -0.0020, 5.00, 0.1810 },
1225 { -0.0020, 4.80, 0.1825 },
1226 { -0.0015, 4.70, 0.1840 },
1227 { -0.0015, 4.60, 0.1860 }, /* 150 */
1228 { -0.0015, 4.40, 0.1880 },
1229 { -0.0015, 4.20, 0.1900 },
1230 { -0.0015, 4.10, 0.1915 },
1231 { -0.0010, 4.00, 0.1935 },
1232 { -0.0010, 3.80, 0.1955 },
1233 { -0.0010, 3.70, 0.1970 },
1234 { -0.0010, 3.50, 0.1990 },
1235 { -0.0005, 3.40, 0.2010 },
1236 { -0.0010, 3.20, 0.2025 },
1237 { -0.0005, 3.10, 0.2045 }, /* 160 */
1238 { -0.0005, 2.90, 0.2065 },
1239 { -0.0005, 2.80, 0.2085 },
1240 { -0.0005, 2.60, 0.2105 },
1241 { -0.0005, 2.50, 0.2120 },
1242 { -0.0005, 2.30, 0.2140 },
1243 { -0.0005, 2.20, 0.2160 },
1244 { -0.0005, 2.00, 0.2180 },
1245 { 0.0000, 1.90, 0.2200 },
1246 { 0.0000, 1.70, 0.2220 },
1247 { 0.0000, 1.60, 0.2235 }, /* 170 */
1248 { 0.0000, 1.40, 0.2255 },
1249 { 0.0000, 1.30, 0.2275 },
1250 { 0.0000, 1.10, 0.2295 },
1251 { 0.0000, 0.90, 0.2315 },
1252 { 0.0000, 0.80, 0.2335 },
1253 { 0.0000, 0.60, 0.2355 },
1254 { 0.0000, 0.50, 0.2375 },
1255 { 0.0000, 0.30, 0.2395 },
1256 { 0.0000, 0.10, 0.2415 },
1257 { 0.0000, 0.00, 0.2430 }, /* 180 */
1258 { 0.0000, -0.10, 0.2450 },
1259 { 0.0000, -0.30, 0.2470 },
1260 { 0.0000, -0.40, 0.2490 },
1261 { 0.0000, -0.60, 0.2510 },
1262 { 0.0000, -0.80, 0.2530 },
1263 { 0.0000, -0.90, 0.2550 },
1264 { 0.0000, -1.10, 0.2570 },
1265 { 0.0000, -1.20, 0.2590 },
1266 { 0.0000, -1.40, 0.2610 },
1267 { 0.0000, -1.50, 0.2625 }, /* 190 */
1268 { 0.0000, -1.70, 0.2645 },
1269 { 0.0000, -1.80, 0.2665 },
1270 { -0.0005, -2.00, 0.2685 },
1271 { -0.0005, -2.10, 0.2705 },
1272 { -0.0005, -2.30, 0.2725 },
1273 { -0.0005, -2.40, 0.2740 },
1274 { -0.0005, -2.60, 0.2760 },
1275 { -0.0005, -2.80, 0.2780 },
1276 { -0.0005, -2.90, 0.2800 },
1277 { -0.0005, -3.00, 0.2820 }, /* 200 */
1278 { -0.0010, -3.20, 0.2835 },
1279 { -0.0005, -3.30, 0.2855 },
1280 { -0.0010, -3.50, 0.2875 },
1281 { -0.0010, -3.70, 0.2895 },
1282 { -0.0010, -3.80, 0.2910 },
1283 { -0.0010, -3.90, 0.2930 },
1284 { -0.0010, -4.00, 0.2950 },
1285 { -0.0015, -4.20, 0.2965 },
1286 { -0.0015, -4.40, 0.2985 },
1287 { -0.0015, -4.50, 0.3000 }, /* 210 */
1288 { -0.0015, -4.60, 0.3020 },
1289 { -0.0020, -4.80, 0.3040 },
1290 { -0.0020, -5.00, 0.3055 },
1291 { -0.0020, -5.00, 0.3075 },
1292 { -0.0025, -5.20, 0.3090 },
1293 { -0.0025, -5.30, 0.3110 },
1294 { -0.0025, -5.50, 0.3125 },
1295 { -0.0025, -5.60, 0.3140 },
1296 { -0.0030, -5.70, 0.3160 },
1297 { -0.0030, -5.90, 0.3175 }, /* 220 */
1298 { -0.0030, -6.00, 0.3190 },
1299 { -0.0035, -6.10, 0.3210 },
1300 { -0.0035, -6.30, 0.3225 },
1301 { -0.0040, -6.40, 0.3240 },
1302 { -0.0040, -6.50, 0.3255 },
1303 { -0.0040, -6.60, 0.3270 },
1304 { -0.0045, -6.80, 0.3290 },
1305 { -0.0045, -6.90, 0.3305 },
1306 { -0.0050, -7.00, 0.3320 },
1307 { -0.0050, -7.20, 0.3335 }, /* 230 */
1308 { -0.0050, -7.20, 0.3350 },
1309 { -0.0055, -7.40, 0.3365 },
1310 { -0.0055, -7.50, 0.3380 },
1311 { -0.0060, -7.60, 0.3390 },
1312 { -0.0060, -7.70, 0.3405 },
1313 { -0.0065, -7.80, 0.3420 },
1314 { -0.0060, -7.60, 0.3425 },
1315 { -0.0060, -7.50, 0.3440 },
1316 { -0.0055, -7.40, 0.3455 },
1317 { -0.0055, -7.20, 0.3470 }, /* 240 */
1318 { -0.0050, -7.10, 0.3480 },
1319 { -0.0050, -6.90, 0.3490 },
1320 { -0.0045, -6.80, 0.3500 },
1321 { -0.0045, -6.50, 0.3510 },
1322 { -0.0040, -6.40, 0.3520 },
1323 { -0.0040, -6.10, 0.3535 },
1324 { -0.0035, -6.00, 0.3545 },
1325 { -0.0035, -5.80, 0.3550 },
1326 { -0.0030, -5.50, 0.3560 },
1327 { -0.0030, -5.30, 0.3570 }, /* 250 */
1328 { -0.0030, -5.10, 0.3580 },
1329 { -0.0030, -4.90, 0.3585 },
1330 { -0.0025, -4.70, 0.3595 },
1331 { -0.0025, -4.40, 0.3600 },
1332 { -0.0020, -4.10, 0.3610 },
1333 { -0.0020, -3.90, 0.3615 },
1334 { -0.0020, -3.70, 0.3620 },
1335 { -0.0020, -3.30, 0.3625 },
1336 { -0.0020, -3.10, 0.3630 },
1337 { -0.0015, -2.80, 0.3635 }, /* 260 */
1338 { -0.0015, -2.60, 0.3640 },
1339 { -0.0015, -2.40, 0.3645 },
1340 { -0.0015, -2.00, 0.3645 },
1341 { -0.0015, -1.80, 0.3650 },
1342 { -0.0015, -1.40, 0.3650 },
1343 { -0.0015, -1.20, 0.3655 },
1344 { -0.0010, -0.90, 0.3655 },
1345 { -0.0010, -0.60, 0.3655 },
1346 { -0.0010, -0.30, 0.3655 },
1347 { -0.0010, 0.00, 0.3655 }, /* 270 */
1348 { -0.0010, 0.30, 0.3655 },
1349 { -0.0010, 0.60, 0.3655 },
1350 { -0.0010, 0.90, 0.3655 },
1351 { -0.0015, 1.10, 0.3655 },
1352 { -0.0015, 1.40, 0.3655 },
1353 { -0.0015, 1.70, 0.3655 },
1354 { -0.0015, 1.90, 0.3660 },
1355 { -0.0015, 2.20, 0.3660 },
1356 { -0.0015, 2.50, 0.3665 },
1357 { -0.0015, 2.80, 0.3670 }, /* 280 */
1358 { -0.0015, 3.10, 0.3675 },
1359 { -0.0020, 3.40, 0.3680 },
1360 { -0.0020, 3.70, 0.3685 },
1361 { -0.0020, 3.90, 0.3690 },
1362 { -0.0025, 4.10, 0.3695 },
1363 { -0.0025, 4.40, 0.3700 },
1364 { -0.0025, 4.60, 0.3710 },
1365 { -0.0025, 4.80, 0.3715 },
1366 { -0.0025, 5.00, 0.3730 },
1367 { -0.0030, 5.40, 0.3735 }, /* 290 */
1368 { -0.0035, 5.60, 0.3745 },
1369 { -0.0035, 5.80, 0.3755 },
1370 { -0.0035, 6.00, 0.3765 },
1371 { -0.0040, 6.20, 0.3775 },
1372 { -0.0045, 6.50, 0.3785 },
1373 { -0.0045, 6.60, 0.3795 },
1374 { -0.0045, 6.80, 0.3805 },
1375 { -0.0050, 7.00, 0.3815 },
1376 { -0.0050, 7.10, 0.3825 },
1377 { -0.0055, 7.20, 0.3840 }, /* 300 */
1378 { -0.0055, 7.40, 0.3850 },
1379 { -0.0060, 7.50, 0.3865 },
1380 { -0.0060, 7.70, 0.3875 },
1381 { -0.0065, 7.80, 0.3890 },
1382 { -0.0060, 7.80, 0.3900 },
1383 { -0.0060, 7.60, 0.3915 },
1384 { -0.0055, 7.60, 0.3930 },
1385 { -0.0055, 7.40, 0.3945 },
1386 { -0.0050, 7.30, 0.3960 },
1387 { -0.0050, 7.20, 0.3975 }, /* 310 */
1388 { -0.0050, 7.00, 0.3990 },
1389 { -0.0045, 6.90, 0.4005 },
1390 { -0.0045, 6.80, 0.4020 },
1391 { -0.0040, 6.70, 0.4035 },
1392 { -0.0040, 6.60, 0.4050 },
1393 { -0.0040, 6.40, 0.4065 },
1394 { -0.0035, 6.30, 0.4085 },
1395 { -0.0035, 6.20, 0.4100 },
1396 { -0.0030, 6.10, 0.4115 },
1397 { -0.0030, 5.90, 0.4130 }, /* 320 */
1398 { -0.0030, 5.80, 0.4150 },
1399 { -0.0025, 5.70, 0.4165 },
1400 { -0.0025, 5.50, 0.4180 },
1401 { -0.0025, 5.40, 0.4200 },
1402 { -0.0025, 5.20, 0.4215 },
1403 { -0.0020, 5.10, 0.4235 },
1404 { -0.0020, 5.00, 0.4250 },
1405 { -0.0020, 4.80, 0.4270 },
1406 { -0.0015, 4.70, 0.4285 },
1407 { -0.0015, 4.60, 0.4305 }, /* 330 */
1408 { -0.0015, 4.40, 0.4325 },
1409 { -0.0015, 4.20, 0.4340 },
1410 { -0.0015, 4.10, 0.4360 },
1411 { -0.0010, 4.00, 0.4375 },
1412 { -0.0010, 3.80, 0.4395 },
1413 { -0.0010, 3.70, 0.4415 },
1414 { -0.0010, 3.50, 0.4435 },
1415 { -0.0005, 3.40, 0.4450 },
1416 { -0.0010, 3.20, 0.4470 },
1417 { -0.0005, 3.10, 0.4490 }, /* 340 */
1418 { -0.0005, 2.90, 0.4510 },
1419 { -0.0005, 2.80, 0.4525 },
1420 { -0.0005, 2.60, 0.4545 },
1421 { -0.0005, 2.40, 0.4565 },
1422 { -0.0005, 2.30, 0.4585 },
1423 { -0.0005, 2.20, 0.4605 },
1424 { -0.0005, 2.00, 0.4620 },
1425 { 0.0000, 1.90, 0.4640 },
1426 { 0.0000, 1.70, 0.4660 },
1427 { 0.0000, 1.60, 0.4680 }, /* 350 */
1428 { 0.0000, 1.40, 0.4700 },
1429 { 0.0000, 1.20, 0.4720 },
1430 { 0.0000, 1.10, 0.4740 },
1431 { 0.0000, 0.90, 0.4760 },
1432 { 0.0000, 0.80, 0.4780 },
1433 { 0.0000, 0.60, 0.4795 },
1434 { 0.0000, 0.50, 0.4815 },
1435 { 0.0000, 0.30, 0.4835 },
1436 { 0.0000, 0.20, 0.4855 }, /* 359 */
1440 /* Turn the crank by 1 degree, which moves the legs and displaces the robot.
1443 tick_walker (ModeInfo *mi, walker *f)
1445 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1449 if (bp->button_down_p) return;
1452 deg = ((int) (f->crank_rot + 0.5)) % 360;
1457 f->crank_rot = bp->debug_x;
1458 f->pitch = wobble_profile[deg].rot;
1459 f->z = wobble_profile[deg].up;
1466 fwd = wobble_profile[deg].fwd;
1467 f->pitch = wobble_profile[deg].rot;
1468 f->z = wobble_profile[deg].up;
1472 fwd = (wobble_profile[deg].fwd - wobble_profile[deg-1].fwd);
1473 f->pitch += (wobble_profile[deg].rot - wobble_profile[deg-1].rot);
1474 f->z += (wobble_profile[deg].up - wobble_profile[deg-1].up);
1477 /* Lean slightly toward the foot that is raised off the ground. */
1478 f->roll = -2.5 * sin ((deg - 90) * M_PI / 180);
1480 if (!(random() % 10))
1482 GLfloat b = f->balance / 10.0;
1483 int s = (f->balance > 0 ? 1 : -1);
1485 f->facing += s * frand (b);
1489 if (debug_p) fwd = 0;
1495 th = f->facing * M_PI / 180.0;
1496 f->x += fwd * cos (th);
1497 f->y += fwd * sin (th);
1499 /* If moving this robot would collide with another, undo the move,
1500 recoil, and randomly turn.
1502 if (collision_p (mi, f, 0))
1505 f->x = ox + fwd * cos (th);
1506 f->y = oy + fwd * sin (th);
1507 f->facing += frand(10) - 5;
1508 if (! random() % 30)
1509 f->facing += frand(90) - 45;
1515 opacity > 0.5) /* Don't bother fading if it's already transparent. */
1517 GLfloat tick = 0.002;
1520 /* If we're not fading, maybe start fading out. */
1521 if (f->fading_p == 0 && ! (random() % 40000))
1525 if (debug_p) f->fading_p = 0;
1528 if (f->fading_p < 0)
1530 f->body_transparency -= tick;
1531 if (f->body_transparency <= -linger)
1533 f->body_transparency = -linger;
1537 else if (f->fading_p > 0)
1539 f->body_transparency += tick;
1540 if (f->body_transparency >= opacity)
1542 f->body_transparency = opacity;
1551 init_walker (ModeInfo *mi, walker *f)
1553 int i, start_tick = random() % 360;
1556 f->pitch = wobble_profile[0].rot;
1557 f->z = wobble_profile[0].up;
1559 f->body_transparency = opacity;
1561 f->hand_rot[0] = frand(180);
1562 f->hand_pos[0] = 0.6 + frand(0.4);
1563 f->hand_rot[1] = 180 - f->hand_rot[0];
1564 f->hand_pos[1] = f->hand_pos[0];
1566 if (! (random() % 30)) f->hand_rot[1] += frand(10);
1567 if (! (random() % 30)) f->hand_pos[1] = 0.6 + frand(0.4);
1569 f->facing = frand(360);
1570 f->balance = frand(10) - 5;
1572 if (MI_COUNT(mi) == 1)
1575 f->speed = 0.6 + frand(0.8);
1586 for (i = 0; i < start_tick; i++)
1587 tick_walker (mi, f);
1589 /* Place them randomly, but non-overlapping. */
1590 for (i = 0; i < 1000; i++)
1593 if (MI_COUNT(mi) > 10) range += MI_COUNT(mi) / 10.0;
1594 f->x = frand(range) - range/2;
1595 f->y = frand(range) - range/2;
1596 if (! collision_p (mi, f, 1.5))
1601 if (debug_p) f->x = f->y = 0;
1607 /* Draw a robot standing in the right place, 1 unit tall.
1610 draw_walker (ModeInfo *mi, walker *f, const char *tag)
1612 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1613 int wire = MI_IS_WIREFRAME(mi);
1617 glTranslatef (f->y, f->z, f->x);
1624 glRotatef (90, 0, 1, 0);
1625 glRotatef (f->facing, 0, 1, 0);
1626 glRotatef (f->pitch, 0, 0, 1);
1627 glRotatef (f->roll, 1, 0, 0);
1630 GLfloat n = 0.00484; /* make it 1 unit tall */
1634 count += draw_gearbox (mi);
1635 count += draw_crank (mi, f, f->crank_rot);
1636 count += draw_rotator (mi, f, f->crank_rot);
1637 count += draw_leg (mi, f->crank_rot, False);
1638 count += draw_leg (mi, f->crank_rot, True);
1639 count += draw_wireframe (mi, f);
1641 /* Draw these last, and outer shell first, to make transparency work.
1642 The order in which things hit the depth buffer matters.
1644 if (f->body_transparency >= 0.001)
1646 count += draw_arm (mi, f, True, f->hand_rot[0], f->hand_pos[0]);
1647 count += draw_arm (mi, f, False, f->hand_rot[1], f->hand_pos[1]);
1648 count += draw_body (mi, f, False);
1649 count += draw_body (mi, f, True);
1650 count += draw_dome (mi, f);
1653 if (tag) /* For debugging depth sorting: label each robot */
1656 if (! wire) glDisable (GL_DEPTH_TEST);
1657 glColor3f (1, 1, 1);
1660 /* Billboard rotation */
1661 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
1662 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
1663 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
1664 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
1666 glMultMatrixf (&m[0][0]);
1667 glScalef (0.04, 0.04, 0.04);
1669 print_texture_string (bp->font_data, tag);
1671 if (! wire) glEnable (GL_DEPTH_TEST);
1680 draw_ground (ModeInfo *mi, GLfloat color[4])
1682 int wire = MI_IS_WIREFRAME(mi);
1685 /* When using fog, iOS apparently doesn't like lines or quads that are
1686 really long, and extend very far outside of the scene. Maybe? If the
1687 length of the line (cells * cell_size) is greater than 25 or so, lines
1688 that are oriented steeply away from the viewer tend to disappear
1689 (whether implemented as GL_LINES or as GL_QUADS).
1691 So we do a bunch of smaller grids instead of one big one.
1694 GLfloat cell_size = 0.8;
1699 if (debug_p) return 0;
1704 glRotatef (frand(90), 0, 0, 1);
1708 GLfloat fog_color[4] = { 0, 0, 0, 1 };
1711 glEnable (GL_LINE_SMOOTH);
1712 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
1713 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1714 glEnable (GL_BLEND);
1716 glFogi (GL_FOG_MODE, GL_EXP2);
1717 glFogfv (GL_FOG_COLOR, fog_color);
1718 glFogf (GL_FOG_DENSITY, 0.015);
1719 glFogf (GL_FOG_START, -cells/2 * cell_size * grids);
1724 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
1726 glTranslatef (-cells * grids * cell_size / 2,
1727 -cells * grids * cell_size / 2, 0);
1729 for (j = 0; j < grids; j++)
1732 for (k = 0; k < grids; k++)
1735 for (i = -cells/2; i < cells/2; i++)
1737 GLfloat a = i * cell_size;
1738 GLfloat b = cells/2 * cell_size;
1739 glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
1740 glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
1743 glTranslatef (cells * cell_size, 0, 0);
1746 glTranslatef (0, cells * cell_size, 0);
1751 glDisable (GL_LINE_SMOOTH);
1752 glDisable (GL_BLEND);
1762 /* If the target robot (robot #0) has moved too far from the point at which
1763 the camera is aimed, then initiate an animation to move the observer to
1766 Because of the jerky forward motion of the robots, just always focusing
1767 on the center of the robot looks terrible, so instead we let them walk
1768 a little out of the center of the frame, and then catch up.
1771 look_at_center (ModeInfo *mi)
1773 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1774 GLfloat target_x = bp->walkers[0].x;
1775 GLfloat target_y = bp->walkers[0].y;
1776 GLfloat target_z = 0.8; /* Look a little bit above his head */
1777 GLfloat max_dist = 2.5 / size;
1780 if (debug_p) return;
1783 if (max_dist < 1) max_dist = 1;
1784 if (max_dist > 10) max_dist = 10;
1786 if (bp->camera_tracking_p)
1788 GLfloat r = (1 - cos (bp->tracking_ratio * M_PI)) / 2;
1789 bp->looking_x = bp->olooking_x + r * (target_x - bp->olooking_x);
1790 bp->looking_y = bp->olooking_y + r * (target_y - bp->olooking_y);
1791 bp->looking_z = bp->olooking_z + r * (target_z - bp->olooking_z);
1793 bp->tracking_ratio += 0.02;
1794 if (bp->tracking_ratio >= 1)
1796 bp->camera_tracking_p = False;
1797 bp->olooking_x = bp->looking_x;
1798 bp->olooking_y = bp->looking_y;
1799 bp->olooking_z = bp->looking_z;
1803 if (! bp->camera_tracking_p)
1806 sqrt ((target_x - bp->looking_x) * (target_x - bp->looking_x) +
1807 (target_y - bp->looking_y) * (target_y - bp->looking_y) +
1808 (target_z - bp->looking_z) * (target_z - bp->looking_z));
1810 if (dist > max_dist)
1812 bp->camera_tracking_p = True;
1813 bp->tracking_ratio = 0;
1814 bp->olooking_x = bp->looking_x;
1815 bp->olooking_y = bp->looking_y;
1816 bp->olooking_z = bp->looking_z;
1820 glTranslatef (-bp->looking_y, -bp->looking_z, -bp->looking_x);
1827 glTranslatef (target_y, target_z, target_x);
1829 glVertex3f(0, -target_z, 0);
1830 glVertex3f(0, 1, 0);
1831 glVertex3f(-0.1, 0, -0.1);
1832 glVertex3f( 0.1, 0, 0.1);
1833 glVertex3f(-0.1, 0, 0.1);
1834 glVertex3f( 0.1, 0, -0.1);
1840 glTranslatef (bp->looking_y, bp->looking_z, bp->looking_x);
1841 glRotatef (30, 0, 1, 0);
1843 glVertex3f(0, -bp->looking_z, 0);
1844 glVertex3f(0, 1, 0);
1845 glVertex3f(-0.1, 0, -0.1);
1846 glVertex3f( 0.1, 0, 0.1);
1847 glVertex3f(-0.1, 0, 0.1);
1848 glVertex3f( 0.1, 0, -0.1);
1854 glTranslatef (bp->olooking_y, bp->olooking_z, bp->olooking_x);
1855 glRotatef (60, 0, 1, 0);
1857 glVertex3f(0, -bp->olooking_z, 0);
1858 glVertex3f(0, 1, 0);
1859 glVertex3f(-0.1, 0, -0.1);
1860 glVertex3f( 0.1, 0, 0.1);
1861 glVertex3f(-0.1, 0, 0.1);
1862 glVertex3f( 0.1, 0, -0.1);
1865 glTranslatef (0, -bp->olooking_z, 0);
1866 glBegin (GL_LINE_LOOP);
1867 for (th = 0; th < M_PI * 2; th += 0.1)
1868 glVertex3f (bp->olooking_y + max_dist * cos(th), 0,
1869 bp->olooking_x + max_dist * sin(th));
1879 /* Draw a cartoony word bubble.
1880 W and H are the inside size, for text.
1881 Origin is at bottom left.
1882 The bubble frame and arrow are outside that.
1885 draw_bubble_box (ModeInfo *mi,
1886 GLfloat width, GLfloat height,
1887 GLfloat corner_radius,
1888 GLfloat arrow_h, GLfloat arrow_x,
1889 GLfloat fg[4], GLfloat bg[4])
1892 # define CORNER_POINTS 16
1893 GLfloat outline_points[ (CORNER_POINTS + 2) * 4 + 8 ][3];
1896 GLfloat tick = M_PI / 2 / CORNER_POINTS;
1898 GLfloat arrow_w = arrow_h / 2;
1899 GLfloat arrow_x2 = MAX(0, MIN(width - arrow_w, arrow_x));
1901 GLfloat w2 = MAX(arrow_w, width - corner_radius * 1.10);
1902 GLfloat h2 = MAX(0, height - corner_radius * 1.28);
1903 GLfloat x2 = (width - w2) / 2;
1904 GLfloat y2 = (height - h2) / 2;
1906 GLfloat xa = x2 -corner_radius; /* E _------------_ */
1907 GLfloat xb = x2; /* D /__| |__\ */
1908 GLfloat xc = xb + w2; /* | | | | */
1909 GLfloat xd = xc + corner_radius; /* C |__| EF |__| */
1910 GLfloat xe = xb + arrow_x2; /* B \_|_________|_/ */
1911 GLfloat xf = xe + arrow_w; /* A \| */
1913 GLfloat ya = y2 - (corner_radius + arrow_h);
1914 GLfloat yb = y2 - corner_radius;
1916 GLfloat yd = yc + h2;
1917 GLfloat ye = yd + corner_radius;
1921 /* Let the lines take precedence over the fills. */
1922 glEnable (GL_POLYGON_OFFSET_FILL);
1923 glPolygonOffset (1.0, 1.0);
1928 /* top left corner */
1930 glBegin (GL_TRIANGLE_FAN);
1931 glVertex3f (xb, yd, 0);
1932 for (th = 0; th < M_PI/2 + tick; th += tick)
1934 GLfloat x = xb - corner_radius * cos(th);
1935 GLfloat y = yd + corner_radius * sin(th);
1936 glVertex3f (x, y, z);
1937 outline_points[i][0] = x;
1938 outline_points[i][1] = y;
1939 outline_points[i][2] = z;
1945 outline_points[i][0] = xc;
1946 outline_points[i][1] = ye;
1947 outline_points[i][2] = z;
1950 /* top right corner */
1952 glBegin (GL_TRIANGLE_FAN);
1953 glVertex3f (xc, yd, 0);
1954 for (th = M_PI/2; th > -tick; th -= tick)
1956 GLfloat x = xc + corner_radius * cos(th);
1957 GLfloat y = yd + corner_radius * sin(th);
1958 glVertex3f (x, y, z);
1959 outline_points[i][0] = x;
1960 outline_points[i][1] = y;
1961 outline_points[i][2] = z;
1967 outline_points[i][0] = xd;
1968 outline_points[i][1] = yc;
1969 outline_points[i][2] = z;
1972 /* bottom right corner */
1974 glBegin (GL_TRIANGLE_FAN);
1975 glVertex3f (xc, yc, 0);
1976 for (th = 0; th < M_PI/2 + tick; th += tick)
1978 GLfloat x = xc + corner_radius * cos(th);
1979 GLfloat y = yc - corner_radius * sin(th);
1980 glVertex3f (x, y, z);
1981 outline_points[i][0] = x;
1982 outline_points[i][1] = y;
1983 outline_points[i][2] = z;
1988 /* bottom right edge */
1989 outline_points[i][0] = xf;
1990 outline_points[i][1] = yb;
1991 outline_points[i][2] = z;
1994 /* arrow triangle */
1996 glBegin (GL_TRIANGLES);
1998 /* bottom arrow point */
1999 outline_points[i][0] = xf;
2000 outline_points[i][1] = yb;
2001 outline_points[i][2] = z;
2002 glVertex3f (outline_points[i][0],
2003 outline_points[i][1],
2004 outline_points[i][2]);
2007 /* bottom right edge */
2008 outline_points[i][0] = xf;
2009 outline_points[i][1] = ya;
2010 outline_points[i][2] = z;
2011 glVertex3f (outline_points[i][0],
2012 outline_points[i][1],
2013 outline_points[i][2]);
2016 outline_points[i][0] = xe;
2017 outline_points[i][1] = yb;
2018 outline_points[i][2] = z;
2019 glVertex3f (outline_points[i][0],
2020 outline_points[i][1],
2021 outline_points[i][2]);
2026 /* bottom left corner */
2028 glBegin (GL_TRIANGLE_FAN);
2029 glVertex3f (xb, yc, 0);
2030 for (th = M_PI/2; th > -tick; th -= tick)
2032 GLfloat x = xb - corner_radius * cos(th);
2033 GLfloat y = yc - corner_radius * sin(th);
2034 glVertex3f (x, y, z);
2035 outline_points[i][0] = x;
2036 outline_points[i][1] = y;
2037 outline_points[i][2] = z;
2042 glFrontFace(GL_CCW);
2045 outline_points[i][0] = xa;
2046 outline_points[i][1] = yd;
2047 outline_points[i][2] = z;
2053 glVertex3f (xa, yd, z);
2054 glVertex3f (xb, yd, z);
2055 glVertex3f (xb, yc, z);
2056 glVertex3f (xa, yc, z);
2059 glVertex3f (xb, ye, z);
2060 glVertex3f (xc, ye, z);
2061 glVertex3f (xc, yb, z);
2062 glVertex3f (xb, yb, z);
2065 glVertex3f (xc, yd, z);
2066 glVertex3f (xd, yd, z);
2067 glVertex3f (xd, yc, z);
2068 glVertex3f (xc, yc, z);
2075 glBegin (GL_LINE_LOOP);
2077 glVertex3fv (outline_points[--i]);
2080 glDisable (GL_POLYGON_OFFSET_FILL);
2085 draw_label (ModeInfo *mi, walker *f, GLfloat y_off, GLfloat scale,
2088 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2089 int wire = MI_IS_WIREFRAME(mi);
2092 if (scale == 0) return;
2095 glDisable (GL_LIGHTING); /* don't light fonts */
2099 /* First, we translate the origin to the center of the robot.
2101 Then we retrieve the prevailing modelview matrix, which
2102 includes any rotation, wandering, and user-trackball-rolling
2105 We set the top 3x3 cells of that matrix to be the identity
2106 matrix. This removes all rotation from the matrix, while
2107 leaving the translation alone. This has the effect of
2108 leaving the prevailing coordinate system perpendicular to
2109 the camera view: were we to draw a square face, it would
2110 be in the plane of the screen.
2112 Now we translate by `size' toward the viewer -- so that the
2113 origin is *just in front* of the ball.
2115 Then we draw the label text, allowing the depth buffer to
2116 do its work: that way, labels on atoms will be occluded
2117 properly when other atoms move in front of them.
2119 This technique (of neutralizing rotation relative to the
2120 observer, after both rotations and translations have been
2121 applied) is known as "billboarding".
2125 glTranslatef(f->y, 0, f->x); /* get matrix */
2127 glTranslatef (0, y_off, 0);
2129 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
2130 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
2131 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
2132 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
2133 glLoadIdentity(); /* reset modelview */
2134 glMultMatrixf (&m[0][0]); /* replace with ours */
2136 glTranslatef (0, 0, 0.1); /* move toward camera */
2138 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2142 int cw, ch, w, h, ascent, descent;
2144 GLfloat max = 24; /* max point size to avoid pixellated text */
2146 /* Let the font be much larger on iPhone */
2147 if (mi->xgwa.height <= 640 || mi->xgwa.width <= 640)
2150 texture_string_metrics (bp->font_data, "X", &e, &ascent, &descent);
2152 ch = ascent + descent;
2154 if (ch > max) s *= max/ch;
2158 texture_string_metrics (bp->font_data, label, &e, 0, 0);
2160 h = e.ascent + e.descent;
2163 glTranslatef (-w/2, h*2/3 + (cw * 7), 0);
2166 glTranslatef (0, -h + (ch * 1.2), -0.1);
2167 draw_bubble_box (mi, w, h,
2168 ch * 2, /* corner radius */
2169 ch * 2.5, /* arrow height */
2170 w / 2 - cw * 8, /* arrow x */
2171 bp->text_bd, bp->text_bg);
2174 glColor4fv (bp->text_color);
2175 glTranslatef (0, ch/2, 0);
2176 print_texture_string (bp->font_data, label);
2181 /* More efficient to always call glEnable() with correct values
2182 than to call glPushAttrib()/glPopAttrib(), since reading
2183 attributes from GL does a round-trip and stalls the pipeline.
2186 glEnable (GL_LIGHTING);
2191 fill_words (ModeInfo *mi)
2193 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2194 char *p = bp->words + strlen(bp->words);
2197 int max = bp->max_lines;
2199 /* Fewer lines on iPhone */
2200 if ((mi->xgwa.height <= 640 || mi->xgwa.width <= 640) &&
2204 for (c = bp->words; c < p; c++)
2208 while (p < bp->words + sizeof(bp->words) - 1 &&
2211 int c = textclient_getc (bp->tc);
2226 bubble (ModeInfo *mi)
2228 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2230 GLfloat fade = 0.015;
2231 int chance = (talk_chance <= 0.0 ? 0 :
2232 talk_chance >= 0.99 ? 1 :
2233 (1-talk_chance) * 1000);
2235 char *s0 = strdup (bp->words);
2239 while (*s == '\n') s++;
2241 while (L > 0 && (s[L-1] == '\n' || s[L-1] == ' ' || s[L-1] == '\t'))
2243 if (! *s) goto DONE;
2246 if (debug_p) goto DONE;
2249 if (chance <= 0) goto DONE;
2251 if (bp->bubble_tick > 0)
2254 if (! bp->bubble_tick)
2258 if (! bp->bubble_tick)
2260 if (!(random() % chance))
2261 bp->bubble_tick = duration;
2266 scale = (bp->bubble_tick < duration * fade
2267 ? bp->bubble_tick / (duration * fade)
2268 : (bp->bubble_tick > duration * (1 - fade)
2269 ? 1 - ((bp->bubble_tick - duration * (1 - fade))
2270 / (duration * fade))
2273 draw_label (mi, &bp->walkers[0], 1.5, scale, s);
2278 #endif /* WORDBUBBLES */
2288 cmp_depth_sorter (const void *aa, const void *bb)
2290 const depth_sorter *a = (depth_sorter *) aa;
2291 const depth_sorter *b = (depth_sorter *) bb;
2292 return (a->d == b->d ? 0 : a->d < b->d ? -1 : 1);
2297 draw_robot (ModeInfo *mi)
2299 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2300 Display *dpy = MI_DISPLAY(mi);
2301 Window window = MI_WINDOW(mi);
2303 depth_sorter *sorted;
2306 if (!bp->glx_context)
2309 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
2311 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2316 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2319 gltrackball_rotate (bp->user_trackball);
2321 glTranslatef (0, -20, 0); /* Move the horizon down the screen */
2323 robot_size = size * 7;
2324 glScalef (robot_size, robot_size, robot_size);
2325 look_at_center (mi);
2328 glScalef (1/robot_size, 1/robot_size, 1/robot_size);
2329 glCallList (bp->dlists[GROUND]);
2340 glTranslatef (0, -1.2, 0);
2342 glRotatef (-43.5, 0, 0, 1);
2343 glRotatef (-90, 0, 1, 0);
2345 /* glTranslatef (bp->debug_z, bp->debug_y, 0); */
2348 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
2349 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
2352 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
2353 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
2354 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
2357 glTranslatef (-0.5, 0, -0.5);
2359 glColor3f (1, 0, 0);
2360 glBegin (GL_LINE_LOOP);
2361 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2362 glVertex3f (0, 1, 1); glVertex3f (0, 1, 0);
2365 glBegin (GL_LINE_LOOP);
2366 glVertex3f (1, 0, 0); glVertex3f (1, 0, 1);
2367 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2371 glColor3f (0.5, 0.5, 0.5);
2372 glFrontFace (GL_CCW);
2374 /* glVertex3f (0, 1, 0); glVertex3f (0, 1, 1); */
2375 /* glVertex3f (1, 1, 1); glVertex3f (1, 1, 0); */
2376 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2377 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2380 glFrontFace (GL_CW);
2382 glVertex3f (0, 1, 0); glVertex3f (0, 1, 1);
2383 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2384 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2385 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2389 glColor3f (1, 0, 0);
2391 glVertex3f (0, 0, 0); glVertex3f (1, 0, 0);
2392 glVertex3f (0, 0, 1); glVertex3f (1, 0, 1);
2393 glVertex3f (0, 1, 0); glVertex3f (1, 1, 0);
2394 glVertex3f (0, 1, 1); glVertex3f (1, 1, 1);
2396 glVertex3f (0, 0, 0); glVertex3f (1, 0, 1);
2397 glVertex3f (0, 0, 1); glVertex3f (1, 0, 0);
2398 glVertex3f (0, 1, 0); glVertex3f (1, 1, 1);
2399 glVertex3f (0, 1, 1); glVertex3f (1, 1, 0);
2402 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2403 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
2410 /* For transparency to work right, we have to draw the robots from
2411 back to front, from the perspective of the observer. So project
2412 the origin of the robot to screen coordinates, and sort that by Z.
2414 sorted = (depth_sorter *) calloc (bp->nwalkers, sizeof (*sorted));
2416 GLdouble m[16], p[16];
2418 glGetDoublev (GL_MODELVIEW_MATRIX, m);
2419 glGetDoublev (GL_PROJECTION_MATRIX, p);
2420 glGetIntegerv (GL_VIEWPORT, v);
2422 for (i = 0; i < bp->nwalkers; i++)
2425 walker *f = &bp->walkers[i];
2426 gluProject (f->y, f->z, f->x, m, p, v, &x, &y, &z);
2430 qsort (sorted, i, sizeof(*sorted), cmp_depth_sorter);
2434 mi->polygon_count = 0;
2435 for (i = 0; i < bp->nwalkers; i++)
2437 int ii = sorted[i].i;
2438 walker *f = &bp->walkers[ii];
2439 int ticks = 22 * speed * f->speed;
2444 if (ticks < 1) ticks = 1;
2445 if (ticks > max) ticks = max;
2449 sprintf (tag, "%.4f, %.4f, %.4f",
2450 bp->debug_x, bp->debug_y, bp->debug_z);
2454 /* sprintf (tag + strlen(tag), " %d\n", ii); */
2455 sprintf (tag + strlen(tag), " %d\n", bp->nwalkers - i - 1);
2456 /* sprintf (tag + strlen(tag), "%.03f\n", sorted[i].d); */
2462 mi->polygon_count += draw_walker (mi, f, tag);
2464 for (ii = 0; ii < ticks; ii++)
2465 tick_walker (mi, f);
2471 if (mi->fps_p) do_fps (mi);
2474 glXSwapBuffers(dpy, window);
2478 free_robot (ModeInfo *mi)
2481 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2483 textclient_close (bp->tc);
2487 XSCREENSAVER_MODULE_2 ("WindupRobot", winduprobot, robot)