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