1 /* winduprobot, Copyright (c) 2014, 2015 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Draws a robot wind-up toy.
13 * I've had this little robot since I was about six years old! When the time
14 * came for us to throw the Cocktail Robotics Grand Challenge at DNA Lounge, I
15 * photographed this robot (holding a tiny martini glass) to make a flyer for
16 * the event. You can see that photo here:
17 * https://www.dnalounge.com/flyers/2014/09/14.html
19 * Then I decided to try and make award statues for the contest by modeling
20 * this robot and 3D-printing it (a robot on a post, with the DNA Lounge
21 * grommet around it.) So I learned Maya and built a model.
23 * Well, that 3D printing idea didn't work out, but since I had the model
24 * already, I exported it and turned it into a screen saver.
26 * The DXF files that Maya exports aren't simple enough for my dxf2gl.pl
27 * script to process, so the exporting process went:
29 * - Save from Maya to OBJ;
30 * - Import OBJ into SketchUp using
31 * http://www.scriptspot.com/sketchup/scripts/obj-importer
32 * - Clean up the model a little bit more;
33 * - Export to DXF with "Millimeters", "Triangles", using
34 * http://www.guitar-list.com/download-software/convert-sketchup-skp-files-dxf-or-stl
36 * We did eventually end up with robotic award statues, but we constructed
37 * them out of mass-produced wind-up robots, rather than 3D printing them:
38 * https://www.dnalounge.com/gallery/2014/09-14/045.html
39 * https://www.youtube.com/watch?v=EZF4ZAAy49g
42 #define LABEL_FONT "-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*"
44 #define DEFAULTS "*delay: 20000 \n" \
46 "*showFPS: False \n" \
47 "*wireframe: False \n" \
48 "*labelFont: " LABEL_FONT "\n" \
49 "*legColor: #AA2222" "\n" \
50 "*armColor: #AA2222" "\n" \
51 "*handColor: #AA2222" "\n" \
52 "*crankColor: #444444" "\n" \
53 "*bodyColor: #7777AA" "\n" \
54 "*domeColor: #7777AA" "\n" \
55 "*insideColor: #DDDDDD" "\n" \
56 "*gearboxColor: #444488" "\n" \
57 "*gearColor: #008877" "\n" \
58 "*wheelColor: #007788" "\n" \
59 "*wireColor: #006600" "\n" \
60 "*groundColor: #0000FF" "\n" \
61 "*textColor: #FFFFFF""\n" \
62 "*textBackground: #444444""\n" \
63 "*textBorderColor: #FFFF88""\n" \
65 "*program: xscreensaver-text\n" \
71 # define refresh_robot 0
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;
240 glViewport (0, 0, (GLint) width, (GLint) height);
242 glMatrixMode(GL_PROJECTION);
244 gluPerspective (40.0, 1/h, 1.0, 250);
246 glMatrixMode(GL_MODELVIEW);
248 gluLookAt( 0, 20, 30,
252 glClear(GL_COLOR_BUFFER_BIT);
256 robot_configuration *bp = &bps[MI_SCREEN(mi)];
257 int w = (width < 800 ? 25 : 40);
260 textclient_reshape (bp->tc, w, h, w, h,
261 /* Passing bp->max_lines isn't actually necessary */
270 robot_handle_event (ModeInfo *mi, XEvent *event)
272 robot_configuration *bp = &bps[MI_SCREEN(mi)];
274 if (gltrackball_event_handler (event, bp->user_trackball,
275 MI_WIDTH (mi), MI_HEIGHT (mi),
279 else if (event->xany.type == KeyPress && debug_p)
283 double n[3] = { 1.0, 0.1, 0.1 };
284 int s = (event->xkey.state & ShiftMask ? 10 : 1);
286 XLookupString (&event->xkey, &c, 1, &keysym, 0);
288 if (keysym == XK_Right) bp->debug_x += n[0] * s;
289 else if (keysym == XK_Left) bp->debug_x -= n[0] * s;
290 else if (keysym == XK_Up) bp->debug_y += n[1] * s;
291 else if (keysym == XK_Down) bp->debug_y -= n[1] * s;
292 else if (c == '=' || c == '+') bp->debug_z += n[2] * s;
293 else if (c == '-' || c == '_') bp->debug_z -= n[2] * s;
294 else if (c == ' ') bp->debug_x = bp->debug_y = bp->debug_z = 0;
295 else if (c == '\n' || c == '\r')
296 fprintf (stderr, "%.4f %.4f %.4f\n",
297 bp->debug_x, bp->debug_y, bp->debug_z);
310 load_textures (ModeInfo *mi)
312 robot_configuration *bp = &bps[MI_SCREEN(mi)];
315 xi = xpm_to_ximage (mi->dpy, mi->xgwa.visual, mi->xgwa.colormap,
319 glGenTextures (1, &bp->chrome_texture);
320 glBindTexture (GL_TEXTURE_2D, bp->chrome_texture);
321 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
322 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
323 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
324 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
325 xi->width, xi->height, 0,
328 GL_UNSIGNED_INT_8_8_8_8_REV,
333 check_gl_error("texture");
335 glEnable(GL_TEXTURE_GEN_S);
336 glEnable(GL_TEXTURE_GEN_T);
337 glEnable(GL_TEXTURE_2D);
340 #endif /* HAVE_TEXTURE */
343 static int unit_gear (ModeInfo *, GLfloat color[4]);
344 static int draw_ground (ModeInfo *, GLfloat color[4]);
345 static void init_walker (ModeInfo *, walker *);
348 parse_color (ModeInfo *mi, char *key, GLfloat color[4])
351 char *string = get_string_resource (mi->dpy, key, "RobotColor");
352 if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
354 fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
359 color[0] = xcolor.red / 65536.0;
360 color[1] = xcolor.green / 65536.0;
361 color[2] = xcolor.blue / 65536.0;
367 init_robot (ModeInfo *mi)
369 robot_configuration *bp;
370 int wire = MI_IS_WIREFRAME(mi);
373 bps = (robot_configuration *)
374 calloc (MI_NUM_SCREENS(mi), sizeof (robot_configuration));
376 fprintf(stderr, "%s: out of memory\n", progname);
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 */
596 /* Let's tilt the floor a little. */
601 gltrackball_start (bp->user_trackball, 0, 500, 1000, 1000);
602 gltrackball_track (bp->user_trackball,
603 0, 500 + (random() % 200) - 100,
610 draw_component (ModeInfo *mi, int i)
612 robot_configuration *bp = &bps[MI_SCREEN(mi)];
613 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
614 bp->component_colors[i]);
615 glCallList (bp->dlists[i]);
616 return (*all_objs[i])->points / 3;
620 draw_transparent_component (ModeInfo *mi, int i, GLfloat alpha)
622 robot_configuration *bp = &bps[MI_SCREEN(mi)];
623 int wire = MI_IS_WIREFRAME(mi);
626 if (alpha < 0) return 0;
627 if (alpha > 1) alpha = 1;
628 bp->component_colors[i][3] = alpha;
630 if (wire || alpha >= 1)
631 return draw_component (mi, i);
633 /* Draw into the depth buffer but not the frame buffer */
634 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
635 count += draw_component (mi, i);
637 /* Now draw into the frame buffer only where there's already depth */
638 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
639 glDepthFunc (GL_EQUAL);
641 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
643 count += draw_component (mi, i);
644 glDepthFunc (GL_LESS);
645 glDisable (GL_BLEND);
652 draw_arm_half (ModeInfo *mi, walker *f)
656 count += draw_transparent_component (mi, ROBOT_ARM, f->body_transparency);
662 draw_hand_half (ModeInfo *mi, walker *f)
666 count += draw_transparent_component (mi, ROBOT_HAND, f->body_transparency);
672 /* rotation of arm: 0-360.
673 openness of fingers: 0.0 - 1.0.
676 draw_arm (ModeInfo *mi, walker *f, GLfloat left_p, GLfloat rot, GLfloat open)
679 GLfloat arm_x = 4766; /* distance from origin to arm axis */
680 GLfloat arm_y = 12212;
682 open *= 5.5; /* scale of finger range */
685 if (debug_p) rot = 0;
691 glTranslatef (0, 0, arm_x * 2);
693 glTranslatef (0, arm_y, -arm_x); /* move to origin */
694 glRotatef (rot, 1, 0, 0);
695 glTranslatef (0, -arm_y, arm_x); /* move back */
698 count += draw_arm_half (mi, f);
701 glTranslatef (0, -arm_y * 2, 0);
703 count += draw_arm_half (mi, f);
706 glTranslatef (0, 0, -arm_x * 2);
709 count += draw_arm_half (mi, f);
712 glTranslatef (0, -arm_y * 2, 0);
714 count += draw_arm_half (mi, f);
717 glTranslatef (0, 0, open);
719 count += draw_hand_half (mi, f);
721 glTranslatef (0, 0, -open);
723 glTranslatef (0, 0, arm_x * 2);
725 glTranslatef (0, 0, open);
726 count += draw_hand_half (mi, f);
734 draw_body_half (ModeInfo *mi, walker *f, Bool inside_p)
737 int which = (inside_p ? ROBOT_BODY_2 : ROBOT_BODY_1);
739 count += draw_transparent_component (mi, which, f->body_transparency);
746 draw_body (ModeInfo *mi, walker *f, Bool inside_p)
752 count += draw_body_half (mi, f, inside_p);
756 count += draw_body_half (mi, f, inside_p);
765 draw_gearbox_half (ModeInfo *mi)
769 count += draw_component (mi, ROBOT_GEARBOX);
776 draw_gearbox (ModeInfo *mi)
782 count += draw_gearbox_half (mi);
786 count += draw_gearbox_half (mi);
794 unit_gear (ModeInfo *mi, GLfloat color[4])
796 int wire = MI_IS_WIREFRAME(mi);
804 g->thickness2 = g->thickness * 0.5;
805 g->thickness3 = g->thickness;
806 g->inner_r = g->r * 0.7;
807 g->inner_r2 = g->r * 0.4;
808 g->inner_r3 = g->r * 0.1;
809 g->size = INVOLUTE_LARGE;
811 g->color[0] = g->color2[0] = color[0];
812 g->color[1] = g->color2[1] = color[1];
813 g->color[2] = g->color2[2] = color[2];
814 g->color[3] = g->color2[3] = color[3];
816 return draw_involute_gear (g, wire);
821 draw_gear (ModeInfo *mi)
826 count += draw_component (mi, ROBOT_GEAR);
832 draw_crank (ModeInfo *mi, walker *f, GLfloat rot)
835 GLfloat origin = 12210.0;
841 glTranslatef (0, origin, 0); /* position at origin */
842 glRotatef (rot, 0, 0, 1);
845 glRotatef (90, 1, 0, 0);
846 count += draw_gear (mi);
849 glTranslatef (0, -origin, 0); /* move back */
852 count += draw_component (mi, ROBOT_CRANK);
861 draw_rotator_half (ModeInfo *mi)
865 count += draw_component (mi, ROBOT_ROTATOR);
872 draw_rotator (ModeInfo *mi, walker *f, GLfloat rot)
875 GLfloat origin = 10093.0;
879 glTranslatef (0, origin, 0); /* position at origin */
880 glRotatef (rot, 0, 0, 1);
883 glRotatef (90, 1, 0, 0);
884 count += draw_gear (mi);
890 glDisable(GL_LIGHTING);
892 glVertex3f(0, 0, -3000);
893 glVertex3f(0, 0, 3000);
895 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
899 glTranslatef (0, -origin, 0); /* move back */
902 count += draw_rotator_half (mi);
906 glRotatef (180, 0, 0, 1);
907 glTranslatef (0, -origin * 2, 0); /* move back */
908 count += draw_rotator_half (mi);
916 draw_leg_half (ModeInfo *mi)
920 count += draw_component (mi, ROBOT_LEG);
927 draw_wireframe (ModeInfo *mi, walker *f)
929 int wire = MI_IS_WIREFRAME(mi);
931 GLfloat alpha = 0.6 - f->body_transparency;
932 if (alpha < 0) return 0;
934 if (!wire) glDisable (GL_LIGHTING);
936 count += draw_transparent_component (mi, ROBOT_WIREFRAME, alpha);
938 if (!wire) glEnable (GL_LIGHTING);
944 draw_leg (ModeInfo *mi, GLfloat rot, Bool left_p)
948 GLfloat leg_distance = 9401; /* distance from ground to leg axis */
949 GLfloat rot_distance = 10110; /* distance from ground to rotator axis */
950 GLfloat pin_distance = 14541; /* distance from ground to stop pin */
951 GLfloat orbit_r = rot_distance - leg_distance; /* radius of rotation */
953 /* Actually it's the bottom of the pin minus its diameter, or something. */
959 glRotatef (180, 0, 1, 0);
961 if (!left_p) rot = -(rot + 180);
965 x = orbit_r * cos (-rot * M_PI / 180);
966 y = orbit_r * sin (-rot * M_PI / 180);
969 /* Rotate the leg by angle B of the right A
970 triangle ABC, where: /:
972 A = position of stop pin / :
973 D = position of rotator wheel's axis , - ~ ~ ~ - ,
974 C = D + y , ' / : ' ,
975 B = D + xy (leg's axis) , / : ,
978 H = dist(A,B) , / D ,
979 O = dist(A,C) , / : ,
980 sin(th) = O/H , / : ,
981 th = asin(O/H) B ~ ~ ~ ~ ~ C .
985 GLfloat Ay = pin_distance - leg_distance;
986 GLfloat Cx = 0, Cy = y;
988 GLfloat dBC = Cx - Bx;
989 GLfloat dAC = Cy - Ay;
990 GLfloat dAB = sqrt (dBC*dBC + dAC*dAC);
991 GLfloat th = asin (dAC / dAB);
992 rot = th / (M_PI / 180.0);
994 if (dBC > 0) rot = 360-rot;
997 glTranslatef (0, orbit_r, 0); /* position on rotator axis */
998 glTranslatef (x, y, 0);
1000 glTranslatef (0, leg_distance, 0); /* position on leg axis */
1001 glRotatef(rot, 0, 0, 1);
1002 glTranslatef (0, -leg_distance, 0); /* move back */
1004 glFrontFace(GL_CCW);
1005 count += draw_leg_half (mi);
1007 glScalef (-1, 1, 1);
1009 count += draw_leg_half (mi);
1017 draw_dome (ModeInfo *mi, walker *f)
1019 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1020 int wire = MI_IS_WIREFRAME(mi);
1021 int which = ROBOT_DOME;
1024 GLfloat trans = f->body_transparency;
1026 GLfloat dome_y = 15290;
1028 if (trans < 0) trans = 0;
1029 if (trans > max) trans = max;
1031 if (!wire) glEnable (GL_BLEND);
1034 glTranslatef (0, dome_y, 0);
1035 glScalef (100, 100, 100);
1036 glRotatef (90, 1, 0, 0);
1037 glTranslatef (0.35, 0, 0);
1039 glFrontFace(GL_CCW);
1040 bp->component_colors[which][3] = trans;
1041 count += draw_component (mi, which);
1044 if (!wire) glDisable (GL_BLEND);
1050 /* Is this robot overlapping any other?
1053 collision_p (ModeInfo *mi, walker *w, GLfloat extra_space)
1055 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1057 if (MI_COUNT(mi) <= 1) return False;
1059 for (i = 0; i < MI_COUNT(mi); i++)
1061 walker *w2 = &bp->walkers[i];
1062 GLfloat min = 0.75 + extra_space;
1064 if (w == w2) continue;
1067 d = (dx*dx + dy*dy);
1068 if (d <= min*min) return True;
1074 /* I couldn't figure out a straightforward trig solution to the
1075 forward/backward tilting that happens as weight passes from one
1076 foot to another, so I just built a tool to eyeball it manually.
1077 { vertical_translation, rotation, forward_translation }
1079 static const struct { GLfloat up, rot, fwd; } wobble_profile[360] = {
1080 { 0.0000, 0.00, 0.0000 }, /* 0 */
1081 { 0.0000, -0.01, 0.0025 },
1082 { 0.0000, -0.25, 0.0040 },
1083 { 0.0000, -0.41, 0.0060 },
1084 { 0.0000, -0.62, 0.0080 },
1085 { 0.0000, -0.80, 0.0095 },
1086 { 0.0000, -0.90, 0.0120 },
1087 { 0.0000, -1.10, 0.0135 },
1088 { 0.0000, -1.25, 0.0150 },
1089 { 0.0000, -1.40, 0.0175 },
1090 { 0.0000, -1.50, 0.0195 }, /* 10 */
1091 { 0.0000, -1.70, 0.0215 },
1092 { 0.0000, -1.80, 0.0230 },
1093 { 0.0000, -2.00, 0.0250 },
1094 { 0.0000, -2.10, 0.0270 },
1095 { -0.0005, -2.30, 0.0290 },
1096 { -0.0005, -2.50, 0.0305 },
1097 { -0.0005, -2.60, 0.0330 },
1098 { -0.0005, -2.70, 0.0330 },
1099 { -0.0005, -2.90, 0.0350 },
1100 { -0.0005, -3.00, 0.0365 }, /* 20 */
1101 { -0.0010, -3.20, 0.0380 },
1102 { -0.0005, -3.30, 0.0400 },
1103 { -0.0010, -3.50, 0.0420 },
1104 { -0.0010, -3.70, 0.0440 },
1105 { -0.0010, -3.80, 0.0460 },
1106 { -0.0010, -3.90, 0.0470 },
1107 { -0.0015, -4.10, 0.0500 },
1108 { -0.0015, -4.20, 0.0515 },
1109 { -0.0015, -4.40, 0.0535 },
1110 { -0.0015, -4.50, 0.0550 }, /* 30 */
1111 { -0.0015, -4.60, 0.0565 },
1112 { -0.0020, -4.80, 0.0585 },
1113 { -0.0020, -5.00, 0.0600 },
1114 { -0.0020, -5.10, 0.0620 },
1115 { -0.0025, -5.20, 0.0635 },
1116 { -0.0025, -5.40, 0.0655 },
1117 { -0.0025, -5.50, 0.0675 },
1118 { -0.0025, -5.60, 0.0690 },
1119 { -0.0030, -5.80, 0.0710 },
1120 { -0.0030, -5.90, 0.0720 }, /* 40 */
1121 { -0.0030, -6.00, 0.0740 },
1122 { -0.0035, -6.10, 0.0760 },
1123 { -0.0035, -6.30, 0.0790 },
1124 { -0.0040, -6.40, 0.0805 },
1125 { -0.0040, -6.50, 0.0820 },
1126 { -0.0040, -6.60, 0.0835 },
1127 { -0.0045, -6.80, 0.0855 },
1128 { -0.0045, -6.90, 0.0870 },
1129 { -0.0050, -7.00, 0.0885 },
1130 { -0.0050, -7.20, 0.0900 }, /* 50 */
1131 { -0.0050, -7.20, 0.0915 },
1132 { -0.0055, -7.40, 0.0930 },
1133 { -0.0055, -7.50, 0.0945 },
1134 { -0.0060, -7.60, 0.0960 },
1135 { -0.0060, -7.70, 0.0970 },
1136 { -0.0065, -7.80, 0.0985 },
1137 { -0.0060, -7.70, 0.0995 },
1138 { -0.0060, -7.60, 0.1010 },
1139 { -0.0060, -7.50, 0.1020 },
1140 { -0.0055, -7.30, 0.1030 }, /* 60 */
1141 { -0.0050, -7.10, 0.1040 },
1142 { -0.0050, -6.90, 0.1050 },
1143 { -0.0045, -6.80, 0.1065 },
1144 { -0.0045, -6.50, 0.1075 },
1145 { -0.0040, -6.40, 0.1085 },
1146 { -0.0040, -6.20, 0.1095 },
1147 { -0.0040, -6.00, 0.1105 },
1148 { -0.0035, -5.80, 0.1115 },
1149 { -0.0030, -5.50, 0.1125 },
1150 { -0.0030, -5.40, 0.1135 }, /* 70 */
1151 { -0.0030, -5.10, 0.1145 },
1152 { -0.0030, -4.90, 0.1150 },
1153 { -0.0025, -4.70, 0.1160 },
1154 { -0.0025, -4.40, 0.1165 },
1155 { -0.0025, -4.20, 0.1175 },
1156 { -0.0020, -3.90, 0.1180 },
1157 { -0.0020, -3.70, 0.1185 },
1158 { -0.0020, -3.40, 0.1190 },
1159 { -0.0020, -3.10, 0.1195 },
1160 { -0.0020, -2.90, 0.1200 }, /* 80 */
1161 { -0.0015, -2.60, 0.1200 },
1162 { -0.0015, -2.30, 0.1205 },
1163 { -0.0015, -2.00, 0.1210 },
1164 { -0.0015, -1.80, 0.1215 },
1165 { -0.0015, -1.50, 0.1215 },
1166 { -0.0015, -1.20, 0.1215 },
1167 { -0.0015, -0.90, 0.1215 },
1168 { -0.0015, -0.60, 0.1215 },
1169 { -0.0015, -0.30, 0.1215 },
1170 { -0.0010, 0.00, 0.1215 }, /* 90 */
1171 { -0.0010, 0.30, 0.1215 },
1172 { -0.0010, 0.60, 0.1215 },
1173 { -0.0010, 0.90, 0.1215 },
1174 { -0.0010, 1.20, 0.1215 },
1175 { -0.0015, 1.40, 0.1215 },
1176 { -0.0015, 1.70, 0.1215 },
1177 { -0.0015, 2.00, 0.1215 },
1178 { -0.0015, 2.30, 0.1215 },
1179 { -0.0015, 2.60, 0.1215 },
1180 { -0.0015, 2.80, 0.1220 }, /* 100 */
1181 { -0.0020, 3.10, 0.1225 },
1182 { -0.0020, 3.30, 0.1230 },
1183 { -0.0020, 3.60, 0.1235 },
1184 { -0.0020, 3.90, 0.1240 },
1185 { -0.0025, 4.10, 0.1245 },
1186 { -0.0025, 4.40, 0.1250 },
1187 { -0.0025, 4.60, 0.1260 },
1188 { -0.0025, 4.90, 0.1265 },
1189 { -0.0030, 5.10, 0.1275 },
1190 { -0.0030, 5.30, 0.1285 }, /* 110 */
1191 { -0.0035, 5.60, 0.1290 },
1192 { -0.0035, 5.80, 0.1300 },
1193 { -0.0035, 6.00, 0.1310 },
1194 { -0.0040, 6.20, 0.1325 },
1195 { -0.0040, 6.40, 0.1335 },
1196 { -0.0045, 6.60, 0.1345 },
1197 { -0.0045, 6.70, 0.1355 },
1198 { -0.0050, 6.90, 0.1365 },
1199 { -0.0050, 7.10, 0.1375 },
1200 { -0.0055, 7.30, 0.1390 }, /* 120 */
1201 { -0.0055, 7.40, 0.1400 },
1202 { -0.0060, 7.50, 0.1415 },
1203 { -0.0065, 8.00, 0.1425 },
1204 { -0.0065, 7.80, 0.1440 },
1205 { -0.0060, 7.80, 0.1455 },
1206 { -0.0060, 7.60, 0.1470 },
1207 { -0.0055, 7.50, 0.1485 },
1208 { -0.0055, 7.40, 0.1500 },
1209 { -0.0050, 7.30, 0.1515 },
1210 { -0.0050, 7.20, 0.1530 }, /* 130 */
1211 { -0.0050, 7.00, 0.1545 },
1212 { -0.0045, 6.90, 0.1560 },
1213 { -0.0045, 6.80, 0.1575 },
1214 { -0.0040, 6.70, 0.1590 },
1215 { -0.0040, 6.50, 0.1605 },
1216 { -0.0040, 6.40, 0.1625 },
1217 { -0.0035, 6.30, 0.1640 },
1218 { -0.0035, 6.10, 0.1655 },
1219 { -0.0030, 6.10, 0.1670 },
1220 { -0.0030, 5.90, 0.1690 }, /* 140 */
1221 { -0.0030, 5.70, 0.1705 },
1222 { -0.0025, 5.70, 0.1720 },
1223 { -0.0025, 5.50, 0.1740 },
1224 { -0.0025, 5.40, 0.1755 },
1225 { -0.0025, 5.20, 0.1775 },
1226 { -0.0020, 5.10, 0.1790 },
1227 { -0.0020, 5.00, 0.1810 },
1228 { -0.0020, 4.80, 0.1825 },
1229 { -0.0015, 4.70, 0.1840 },
1230 { -0.0015, 4.60, 0.1860 }, /* 150 */
1231 { -0.0015, 4.40, 0.1880 },
1232 { -0.0015, 4.20, 0.1900 },
1233 { -0.0015, 4.10, 0.1915 },
1234 { -0.0010, 4.00, 0.1935 },
1235 { -0.0010, 3.80, 0.1955 },
1236 { -0.0010, 3.70, 0.1970 },
1237 { -0.0010, 3.50, 0.1990 },
1238 { -0.0005, 3.40, 0.2010 },
1239 { -0.0010, 3.20, 0.2025 },
1240 { -0.0005, 3.10, 0.2045 }, /* 160 */
1241 { -0.0005, 2.90, 0.2065 },
1242 { -0.0005, 2.80, 0.2085 },
1243 { -0.0005, 2.60, 0.2105 },
1244 { -0.0005, 2.50, 0.2120 },
1245 { -0.0005, 2.30, 0.2140 },
1246 { -0.0005, 2.20, 0.2160 },
1247 { -0.0005, 2.00, 0.2180 },
1248 { 0.0000, 1.90, 0.2200 },
1249 { 0.0000, 1.70, 0.2220 },
1250 { 0.0000, 1.60, 0.2235 }, /* 170 */
1251 { 0.0000, 1.40, 0.2255 },
1252 { 0.0000, 1.30, 0.2275 },
1253 { 0.0000, 1.10, 0.2295 },
1254 { 0.0000, 0.90, 0.2315 },
1255 { 0.0000, 0.80, 0.2335 },
1256 { 0.0000, 0.60, 0.2355 },
1257 { 0.0000, 0.50, 0.2375 },
1258 { 0.0000, 0.30, 0.2395 },
1259 { 0.0000, 0.10, 0.2415 },
1260 { 0.0000, 0.00, 0.2430 }, /* 180 */
1261 { 0.0000, -0.10, 0.2450 },
1262 { 0.0000, -0.30, 0.2470 },
1263 { 0.0000, -0.40, 0.2490 },
1264 { 0.0000, -0.60, 0.2510 },
1265 { 0.0000, -0.80, 0.2530 },
1266 { 0.0000, -0.90, 0.2550 },
1267 { 0.0000, -1.10, 0.2570 },
1268 { 0.0000, -1.20, 0.2590 },
1269 { 0.0000, -1.40, 0.2610 },
1270 { 0.0000, -1.50, 0.2625 }, /* 190 */
1271 { 0.0000, -1.70, 0.2645 },
1272 { 0.0000, -1.80, 0.2665 },
1273 { -0.0005, -2.00, 0.2685 },
1274 { -0.0005, -2.10, 0.2705 },
1275 { -0.0005, -2.30, 0.2725 },
1276 { -0.0005, -2.40, 0.2740 },
1277 { -0.0005, -2.60, 0.2760 },
1278 { -0.0005, -2.80, 0.2780 },
1279 { -0.0005, -2.90, 0.2800 },
1280 { -0.0005, -3.00, 0.2820 }, /* 200 */
1281 { -0.0010, -3.20, 0.2835 },
1282 { -0.0005, -3.30, 0.2855 },
1283 { -0.0010, -3.50, 0.2875 },
1284 { -0.0010, -3.70, 0.2895 },
1285 { -0.0010, -3.80, 0.2910 },
1286 { -0.0010, -3.90, 0.2930 },
1287 { -0.0010, -4.00, 0.2950 },
1288 { -0.0015, -4.20, 0.2965 },
1289 { -0.0015, -4.40, 0.2985 },
1290 { -0.0015, -4.50, 0.3000 }, /* 210 */
1291 { -0.0015, -4.60, 0.3020 },
1292 { -0.0020, -4.80, 0.3040 },
1293 { -0.0020, -5.00, 0.3055 },
1294 { -0.0020, -5.00, 0.3075 },
1295 { -0.0025, -5.20, 0.3090 },
1296 { -0.0025, -5.30, 0.3110 },
1297 { -0.0025, -5.50, 0.3125 },
1298 { -0.0025, -5.60, 0.3140 },
1299 { -0.0030, -5.70, 0.3160 },
1300 { -0.0030, -5.90, 0.3175 }, /* 220 */
1301 { -0.0030, -6.00, 0.3190 },
1302 { -0.0035, -6.10, 0.3210 },
1303 { -0.0035, -6.30, 0.3225 },
1304 { -0.0040, -6.40, 0.3240 },
1305 { -0.0040, -6.50, 0.3255 },
1306 { -0.0040, -6.60, 0.3270 },
1307 { -0.0045, -6.80, 0.3290 },
1308 { -0.0045, -6.90, 0.3305 },
1309 { -0.0050, -7.00, 0.3320 },
1310 { -0.0050, -7.20, 0.3335 }, /* 230 */
1311 { -0.0050, -7.20, 0.3350 },
1312 { -0.0055, -7.40, 0.3365 },
1313 { -0.0055, -7.50, 0.3380 },
1314 { -0.0060, -7.60, 0.3390 },
1315 { -0.0060, -7.70, 0.3405 },
1316 { -0.0065, -7.80, 0.3420 },
1317 { -0.0060, -7.60, 0.3425 },
1318 { -0.0060, -7.50, 0.3440 },
1319 { -0.0055, -7.40, 0.3455 },
1320 { -0.0055, -7.20, 0.3470 }, /* 240 */
1321 { -0.0050, -7.10, 0.3480 },
1322 { -0.0050, -6.90, 0.3490 },
1323 { -0.0045, -6.80, 0.3500 },
1324 { -0.0045, -6.50, 0.3510 },
1325 { -0.0040, -6.40, 0.3520 },
1326 { -0.0040, -6.10, 0.3535 },
1327 { -0.0035, -6.00, 0.3545 },
1328 { -0.0035, -5.80, 0.3550 },
1329 { -0.0030, -5.50, 0.3560 },
1330 { -0.0030, -5.30, 0.3570 }, /* 250 */
1331 { -0.0030, -5.10, 0.3580 },
1332 { -0.0030, -4.90, 0.3585 },
1333 { -0.0025, -4.70, 0.3595 },
1334 { -0.0025, -4.40, 0.3600 },
1335 { -0.0020, -4.10, 0.3610 },
1336 { -0.0020, -3.90, 0.3615 },
1337 { -0.0020, -3.70, 0.3620 },
1338 { -0.0020, -3.30, 0.3625 },
1339 { -0.0020, -3.10, 0.3630 },
1340 { -0.0015, -2.80, 0.3635 }, /* 260 */
1341 { -0.0015, -2.60, 0.3640 },
1342 { -0.0015, -2.40, 0.3645 },
1343 { -0.0015, -2.00, 0.3645 },
1344 { -0.0015, -1.80, 0.3650 },
1345 { -0.0015, -1.40, 0.3650 },
1346 { -0.0015, -1.20, 0.3655 },
1347 { -0.0010, -0.90, 0.3655 },
1348 { -0.0010, -0.60, 0.3655 },
1349 { -0.0010, -0.30, 0.3655 },
1350 { -0.0010, 0.00, 0.3655 }, /* 270 */
1351 { -0.0010, 0.30, 0.3655 },
1352 { -0.0010, 0.60, 0.3655 },
1353 { -0.0010, 0.90, 0.3655 },
1354 { -0.0015, 1.10, 0.3655 },
1355 { -0.0015, 1.40, 0.3655 },
1356 { -0.0015, 1.70, 0.3655 },
1357 { -0.0015, 1.90, 0.3660 },
1358 { -0.0015, 2.20, 0.3660 },
1359 { -0.0015, 2.50, 0.3665 },
1360 { -0.0015, 2.80, 0.3670 }, /* 280 */
1361 { -0.0015, 3.10, 0.3675 },
1362 { -0.0020, 3.40, 0.3680 },
1363 { -0.0020, 3.70, 0.3685 },
1364 { -0.0020, 3.90, 0.3690 },
1365 { -0.0025, 4.10, 0.3695 },
1366 { -0.0025, 4.40, 0.3700 },
1367 { -0.0025, 4.60, 0.3710 },
1368 { -0.0025, 4.80, 0.3715 },
1369 { -0.0025, 5.00, 0.3730 },
1370 { -0.0030, 5.40, 0.3735 }, /* 290 */
1371 { -0.0035, 5.60, 0.3745 },
1372 { -0.0035, 5.80, 0.3755 },
1373 { -0.0035, 6.00, 0.3765 },
1374 { -0.0040, 6.20, 0.3775 },
1375 { -0.0045, 6.50, 0.3785 },
1376 { -0.0045, 6.60, 0.3795 },
1377 { -0.0045, 6.80, 0.3805 },
1378 { -0.0050, 7.00, 0.3815 },
1379 { -0.0050, 7.10, 0.3825 },
1380 { -0.0055, 7.20, 0.3840 }, /* 300 */
1381 { -0.0055, 7.40, 0.3850 },
1382 { -0.0060, 7.50, 0.3865 },
1383 { -0.0060, 7.70, 0.3875 },
1384 { -0.0065, 7.80, 0.3890 },
1385 { -0.0060, 7.80, 0.3900 },
1386 { -0.0060, 7.60, 0.3915 },
1387 { -0.0055, 7.60, 0.3930 },
1388 { -0.0055, 7.40, 0.3945 },
1389 { -0.0050, 7.30, 0.3960 },
1390 { -0.0050, 7.20, 0.3975 }, /* 310 */
1391 { -0.0050, 7.00, 0.3990 },
1392 { -0.0045, 6.90, 0.4005 },
1393 { -0.0045, 6.80, 0.4020 },
1394 { -0.0040, 6.70, 0.4035 },
1395 { -0.0040, 6.60, 0.4050 },
1396 { -0.0040, 6.40, 0.4065 },
1397 { -0.0035, 6.30, 0.4085 },
1398 { -0.0035, 6.20, 0.4100 },
1399 { -0.0030, 6.10, 0.4115 },
1400 { -0.0030, 5.90, 0.4130 }, /* 320 */
1401 { -0.0030, 5.80, 0.4150 },
1402 { -0.0025, 5.70, 0.4165 },
1403 { -0.0025, 5.50, 0.4180 },
1404 { -0.0025, 5.40, 0.4200 },
1405 { -0.0025, 5.20, 0.4215 },
1406 { -0.0020, 5.10, 0.4235 },
1407 { -0.0020, 5.00, 0.4250 },
1408 { -0.0020, 4.80, 0.4270 },
1409 { -0.0015, 4.70, 0.4285 },
1410 { -0.0015, 4.60, 0.4305 }, /* 330 */
1411 { -0.0015, 4.40, 0.4325 },
1412 { -0.0015, 4.20, 0.4340 },
1413 { -0.0015, 4.10, 0.4360 },
1414 { -0.0010, 4.00, 0.4375 },
1415 { -0.0010, 3.80, 0.4395 },
1416 { -0.0010, 3.70, 0.4415 },
1417 { -0.0010, 3.50, 0.4435 },
1418 { -0.0005, 3.40, 0.4450 },
1419 { -0.0010, 3.20, 0.4470 },
1420 { -0.0005, 3.10, 0.4490 }, /* 340 */
1421 { -0.0005, 2.90, 0.4510 },
1422 { -0.0005, 2.80, 0.4525 },
1423 { -0.0005, 2.60, 0.4545 },
1424 { -0.0005, 2.40, 0.4565 },
1425 { -0.0005, 2.30, 0.4585 },
1426 { -0.0005, 2.20, 0.4605 },
1427 { -0.0005, 2.00, 0.4620 },
1428 { 0.0000, 1.90, 0.4640 },
1429 { 0.0000, 1.70, 0.4660 },
1430 { 0.0000, 1.60, 0.4680 }, /* 350 */
1431 { 0.0000, 1.40, 0.4700 },
1432 { 0.0000, 1.20, 0.4720 },
1433 { 0.0000, 1.10, 0.4740 },
1434 { 0.0000, 0.90, 0.4760 },
1435 { 0.0000, 0.80, 0.4780 },
1436 { 0.0000, 0.60, 0.4795 },
1437 { 0.0000, 0.50, 0.4815 },
1438 { 0.0000, 0.30, 0.4835 },
1439 { 0.0000, 0.20, 0.4855 }, /* 359 */
1443 /* Turn the crank by 1 degree, which moves the legs and displaces the robot.
1446 tick_walker (ModeInfo *mi, walker *f)
1448 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1452 if (bp->button_down_p) return;
1455 deg = ((int) (f->crank_rot + 0.5)) % 360;
1460 f->crank_rot = bp->debug_x;
1461 f->pitch = wobble_profile[deg].rot;
1462 f->z = wobble_profile[deg].up;
1469 fwd = wobble_profile[deg].fwd;
1470 f->pitch = wobble_profile[deg].rot;
1471 f->z = wobble_profile[deg].up;
1475 fwd = (wobble_profile[deg].fwd - wobble_profile[deg-1].fwd);
1476 f->pitch += (wobble_profile[deg].rot - wobble_profile[deg-1].rot);
1477 f->z += (wobble_profile[deg].up - wobble_profile[deg-1].up);
1480 /* Lean slightly toward the foot that is raised off the ground. */
1481 f->roll = -2.5 * sin ((deg - 90) * M_PI / 180);
1483 if (!(random() % 10))
1485 GLfloat b = f->balance / 10.0;
1486 int s = (f->balance > 0 ? 1 : -1);
1488 f->facing += s * frand (b);
1492 if (debug_p) fwd = 0;
1498 th = f->facing * M_PI / 180.0;
1499 f->x += fwd * cos (th);
1500 f->y += fwd * sin (th);
1502 /* If moving this robot would collide with another, undo the move,
1503 recoil, and randomly turn.
1505 if (collision_p (mi, f, 0))
1508 f->x = ox + fwd * cos (th);
1509 f->y = oy + fwd * sin (th);
1510 f->facing += frand(10) - 5;
1511 if (! random() % 30)
1512 f->facing += frand(90) - 45;
1518 opacity > 0.5) /* Don't bother fading if it's already transparent. */
1520 GLfloat tick = 0.002;
1523 /* If we're not fading, maybe start fading out. */
1524 if (f->fading_p == 0 && ! (random() % 40000))
1528 if (debug_p) f->fading_p = 0;
1531 if (f->fading_p < 0)
1533 f->body_transparency -= tick;
1534 if (f->body_transparency <= -linger)
1536 f->body_transparency = -linger;
1540 else if (f->fading_p > 0)
1542 f->body_transparency += tick;
1543 if (f->body_transparency >= opacity)
1545 f->body_transparency = opacity;
1554 init_walker (ModeInfo *mi, walker *f)
1556 int i, start_tick = random() % 360;
1559 f->pitch = wobble_profile[0].rot;
1560 f->z = wobble_profile[0].up;
1562 f->body_transparency = opacity;
1564 f->hand_rot[0] = frand(180);
1565 f->hand_pos[0] = 0.6 + frand(0.4);
1566 f->hand_rot[1] = 180 - f->hand_rot[0];
1567 f->hand_pos[1] = f->hand_pos[0];
1569 if (! (random() % 30)) f->hand_rot[1] += frand(10);
1570 if (! (random() % 30)) f->hand_pos[1] = 0.6 + frand(0.4);
1572 f->facing = frand(360);
1573 f->balance = frand(10) - 5;
1575 if (MI_COUNT(mi) == 1)
1578 f->speed = 0.6 + frand(0.8);
1589 for (i = 0; i < start_tick; i++)
1590 tick_walker (mi, f);
1592 /* Place them randomly, but non-overlapping. */
1593 for (i = 0; i < 1000; i++)
1596 if (MI_COUNT(mi) > 10) range += MI_COUNT(mi) / 10.0;
1597 f->x = frand(range) - range/2;
1598 f->y = frand(range) - range/2;
1599 if (! collision_p (mi, f, 1.5))
1604 if (debug_p) f->x = f->y = 0;
1610 /* Draw a robot standing in the right place, 1 unit tall.
1613 draw_walker (ModeInfo *mi, walker *f, const char *tag)
1615 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1616 int wire = MI_IS_WIREFRAME(mi);
1620 glTranslatef (f->y, f->z, f->x);
1627 glRotatef (90, 0, 1, 0);
1628 glRotatef (f->facing, 0, 1, 0);
1629 glRotatef (f->pitch, 0, 0, 1);
1630 glRotatef (f->roll, 1, 0, 0);
1633 GLfloat n = 0.00484; /* make it 1 unit tall */
1637 count += draw_gearbox (mi);
1638 count += draw_crank (mi, f, f->crank_rot);
1639 count += draw_rotator (mi, f, f->crank_rot);
1640 count += draw_leg (mi, f->crank_rot, False);
1641 count += draw_leg (mi, f->crank_rot, True);
1642 count += draw_wireframe (mi, f);
1644 /* Draw these last, and outer shell first, to make transparency work.
1645 The order in which things hit the depth buffer matters.
1647 if (f->body_transparency >= 0.001)
1649 count += draw_arm (mi, f, True, f->hand_rot[0], f->hand_pos[0]);
1650 count += draw_arm (mi, f, False, f->hand_rot[1], f->hand_pos[1]);
1651 count += draw_body (mi, f, False);
1652 count += draw_body (mi, f, True);
1653 count += draw_dome (mi, f);
1656 if (tag) /* For debugging depth sorting: label each robot */
1659 if (! wire) glDisable (GL_DEPTH_TEST);
1660 glColor3f (1, 1, 1);
1663 /* Billboard rotation */
1664 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]);
1665 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
1666 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
1667 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
1669 glMultMatrixf (&m[0][0]);
1670 glScalef (0.04, 0.04, 0.04);
1672 print_texture_string (bp->font_data, tag);
1674 if (! wire) glEnable (GL_DEPTH_TEST);
1683 draw_ground (ModeInfo *mi, GLfloat color[4])
1685 int wire = MI_IS_WIREFRAME(mi);
1687 GLfloat cell_size = 0.9;
1688 int cells = 1000 * size;
1692 if (debug_p) return 0;
1697 glRotatef (frand(90), 0, 0, 1);
1701 GLfloat fog_color[4] = { 0, 0, 0, 1 };
1704 glEnable (GL_LINE_SMOOTH);
1705 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
1706 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1707 glEnable (GL_BLEND);
1709 glFogi (GL_FOG_MODE, GL_EXP2);
1710 glFogfv (GL_FOG_COLOR, fog_color);
1711 glFogf (GL_FOG_DENSITY, 0.017);
1712 glFogf (GL_FOG_START, -cells/2 * cell_size);
1713 # ifndef USE_IPHONE /* #### Not working on iOS for some reason */
1719 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
1722 for (i = -cells/2; i < cells/2; i++)
1724 GLfloat a = i * cell_size;
1725 GLfloat b = cells/2 * cell_size;
1726 glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
1727 glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
1733 glDisable (GL_LINE_SMOOTH);
1734 glDisable (GL_BLEND);
1744 /* If the target robot (robot #0) has moved too far from the point at which
1745 the camera is aimed, then initiate an animation to move the observer to
1748 Because of the jerky forward motion of the robots, just always focusing
1749 on the center of the robot looks terrible, so instead we let them walk
1750 a little out of the center of the frame, and then catch up.
1753 look_at_center (ModeInfo *mi)
1755 robot_configuration *bp = &bps[MI_SCREEN(mi)];
1756 GLfloat target_x = bp->walkers[0].x;
1757 GLfloat target_y = bp->walkers[0].y;
1758 GLfloat target_z = 0.8; /* Look a little bit above his head */
1759 GLfloat max_dist = 2.5 / size;
1762 if (debug_p) return;
1765 if (max_dist < 1) max_dist = 1;
1766 if (max_dist > 10) max_dist = 10;
1768 if (bp->camera_tracking_p)
1770 GLfloat r = (1 - cos (bp->tracking_ratio * M_PI)) / 2;
1771 bp->looking_x = bp->olooking_x + r * (target_x - bp->olooking_x);
1772 bp->looking_y = bp->olooking_y + r * (target_y - bp->olooking_y);
1773 bp->looking_z = bp->olooking_z + r * (target_z - bp->olooking_z);
1775 bp->tracking_ratio += 0.02;
1776 if (bp->tracking_ratio >= 1)
1778 bp->camera_tracking_p = False;
1779 bp->olooking_x = bp->looking_x;
1780 bp->olooking_y = bp->looking_y;
1781 bp->olooking_z = bp->looking_z;
1785 if (! bp->camera_tracking_p)
1788 sqrt ((target_x - bp->looking_x) * (target_x - bp->looking_x) +
1789 (target_y - bp->looking_y) * (target_y - bp->looking_y) +
1790 (target_z - bp->looking_z) * (target_z - bp->looking_z));
1792 if (dist > max_dist)
1794 bp->camera_tracking_p = True;
1795 bp->tracking_ratio = 0;
1796 bp->olooking_x = bp->looking_x;
1797 bp->olooking_y = bp->looking_y;
1798 bp->olooking_z = bp->looking_z;
1802 glTranslatef (-bp->looking_y, -bp->looking_z, -bp->looking_x);
1809 glTranslatef (target_y, target_z, target_x);
1811 glVertex3f(0, -target_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);
1822 glTranslatef (bp->looking_y, bp->looking_z, bp->looking_x);
1823 glRotatef (30, 0, 1, 0);
1825 glVertex3f(0, -bp->looking_z, 0);
1826 glVertex3f(0, 1, 0);
1827 glVertex3f(-0.1, 0, -0.1);
1828 glVertex3f( 0.1, 0, 0.1);
1829 glVertex3f(-0.1, 0, 0.1);
1830 glVertex3f( 0.1, 0, -0.1);
1836 glTranslatef (bp->olooking_y, bp->olooking_z, bp->olooking_x);
1837 glRotatef (60, 0, 1, 0);
1839 glVertex3f(0, -bp->olooking_z, 0);
1840 glVertex3f(0, 1, 0);
1841 glVertex3f(-0.1, 0, -0.1);
1842 glVertex3f( 0.1, 0, 0.1);
1843 glVertex3f(-0.1, 0, 0.1);
1844 glVertex3f( 0.1, 0, -0.1);
1847 glTranslatef (0, -bp->olooking_z, 0);
1848 glBegin (GL_LINE_LOOP);
1849 for (th = 0; th < M_PI * 2; th += 0.1)
1850 glVertex3f (bp->olooking_y + max_dist * cos(th), 0,
1851 bp->olooking_x + max_dist * sin(th));
1861 /* Draw a cartoony word bubble.
1862 W and H are the inside size, for text.
1863 Origin is at bottom left.
1864 The bubble frame and arrow are outside that.
1867 draw_bubble_box (ModeInfo *mi,
1868 GLfloat width, GLfloat height,
1869 GLfloat corner_radius,
1870 GLfloat arrow_h, GLfloat arrow_x,
1871 GLfloat fg[4], GLfloat bg[4])
1874 # define CORNER_POINTS 16
1875 GLfloat outline_points[ (CORNER_POINTS + 2) * 4 + 8 ][3];
1878 GLfloat tick = M_PI / 2 / CORNER_POINTS;
1880 GLfloat arrow_w = arrow_h / 2;
1881 GLfloat arrow_x2 = MAX(0, MIN(width - arrow_w, arrow_x));
1883 GLfloat w2 = MAX(arrow_w, width - corner_radius * 1.10);
1884 GLfloat h2 = MAX(0, height - corner_radius * 1.28);
1885 GLfloat x2 = (width - w2) / 2;
1886 GLfloat y2 = (height - h2) / 2;
1888 GLfloat xa = x2 -corner_radius; /* E _------------_ */
1889 GLfloat xb = x2; /* D /__| |__\ */
1890 GLfloat xc = xb + w2; /* | | | | */
1891 GLfloat xd = xc + corner_radius; /* C |__| EF |__| */
1892 GLfloat xe = xb + arrow_x2; /* B \_|_________|_/ */
1893 GLfloat xf = xe + arrow_w; /* A \| */
1895 GLfloat ya = y2 - (corner_radius + arrow_h);
1896 GLfloat yb = y2 - corner_radius;
1898 GLfloat yd = yc + h2;
1899 GLfloat ye = yd + corner_radius;
1903 /* Let the lines take precedence over the fills. */
1904 glEnable (GL_POLYGON_OFFSET_FILL);
1905 glPolygonOffset (1.0, 1.0);
1910 /* top left corner */
1912 glBegin (GL_TRIANGLE_FAN);
1913 glVertex3f (xb, yd, 0);
1914 for (th = 0; th < M_PI/2 + tick; th += tick)
1916 GLfloat x = xb - corner_radius * cos(th);
1917 GLfloat y = yd + corner_radius * sin(th);
1918 glVertex3f (x, y, z);
1919 outline_points[i][0] = x;
1920 outline_points[i][1] = y;
1921 outline_points[i][2] = z;
1927 outline_points[i][0] = xc;
1928 outline_points[i][1] = ye;
1929 outline_points[i][2] = z;
1932 /* top right corner */
1934 glBegin (GL_TRIANGLE_FAN);
1935 glVertex3f (xc, yd, 0);
1936 for (th = M_PI/2; th > -tick; th -= tick)
1938 GLfloat x = xc + corner_radius * cos(th);
1939 GLfloat y = yd + corner_radius * sin(th);
1940 glVertex3f (x, y, z);
1941 outline_points[i][0] = x;
1942 outline_points[i][1] = y;
1943 outline_points[i][2] = z;
1949 outline_points[i][0] = xd;
1950 outline_points[i][1] = yc;
1951 outline_points[i][2] = z;
1954 /* bottom right corner */
1956 glBegin (GL_TRIANGLE_FAN);
1957 glVertex3f (xc, yc, 0);
1958 for (th = 0; th < M_PI/2 + tick; th += tick)
1960 GLfloat x = xc + corner_radius * cos(th);
1961 GLfloat y = yc - corner_radius * sin(th);
1962 glVertex3f (x, y, z);
1963 outline_points[i][0] = x;
1964 outline_points[i][1] = y;
1965 outline_points[i][2] = z;
1970 /* bottom right edge */
1971 outline_points[i][0] = xf;
1972 outline_points[i][1] = yb;
1973 outline_points[i][2] = z;
1976 /* arrow triangle */
1978 glBegin (GL_TRIANGLES);
1980 /* bottom arrow point */
1981 outline_points[i][0] = xf;
1982 outline_points[i][1] = yb;
1983 outline_points[i][2] = z;
1984 glVertex3f (outline_points[i][0],
1985 outline_points[i][1],
1986 outline_points[i][2]);
1989 /* bottom right edge */
1990 outline_points[i][0] = xf;
1991 outline_points[i][1] = ya;
1992 outline_points[i][2] = z;
1993 glVertex3f (outline_points[i][0],
1994 outline_points[i][1],
1995 outline_points[i][2]);
1998 outline_points[i][0] = xe;
1999 outline_points[i][1] = yb;
2000 outline_points[i][2] = z;
2001 glVertex3f (outline_points[i][0],
2002 outline_points[i][1],
2003 outline_points[i][2]);
2008 /* bottom left corner */
2010 glBegin (GL_TRIANGLE_FAN);
2011 glVertex3f (xb, yc, 0);
2012 for (th = M_PI/2; th > -tick; th -= tick)
2014 GLfloat x = xb - corner_radius * cos(th);
2015 GLfloat y = yc - corner_radius * sin(th);
2016 glVertex3f (x, y, z);
2017 outline_points[i][0] = x;
2018 outline_points[i][1] = y;
2019 outline_points[i][2] = z;
2024 glFrontFace(GL_CCW);
2027 outline_points[i][0] = xa;
2028 outline_points[i][1] = yd;
2029 outline_points[i][2] = z;
2035 glVertex3f (xa, yd, z);
2036 glVertex3f (xb, yd, z);
2037 glVertex3f (xb, yc, z);
2038 glVertex3f (xa, yc, z);
2041 glVertex3f (xb, ye, z);
2042 glVertex3f (xc, ye, z);
2043 glVertex3f (xc, yb, z);
2044 glVertex3f (xb, yb, z);
2047 glVertex3f (xc, yd, z);
2048 glVertex3f (xd, yd, z);
2049 glVertex3f (xd, yc, z);
2050 glVertex3f (xc, yc, z);
2057 glBegin (GL_LINE_LOOP);
2059 glVertex3fv (outline_points[--i]);
2062 glDisable (GL_POLYGON_OFFSET_FILL);
2067 draw_label (ModeInfo *mi, walker *f, GLfloat y_off, GLfloat scale,
2070 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2071 int wire = MI_IS_WIREFRAME(mi);
2074 if (scale == 0) return;
2077 glDisable (GL_LIGHTING); /* don't light fonts */
2081 /* First, we translate the origin to the center of the robot.
2083 Then we retrieve the prevailing modelview matrix, which
2084 includes any rotation, wandering, and user-trackball-rolling
2087 We set the top 3x3 cells of that matrix to be the identity
2088 matrix. This removes all rotation from the matrix, while
2089 leaving the translation alone. This has the effect of
2090 leaving the prevailing coordinate system perpendicular to
2091 the camera view: were we to draw a square face, it would
2092 be in the plane of the screen.
2094 Now we translate by `size' toward the viewer -- so that the
2095 origin is *just in front* of the ball.
2097 Then we draw the label text, allowing the depth buffer to
2098 do its work: that way, labels on atoms will be occluded
2099 properly when other atoms move in front of them.
2101 This technique (of neutralizing rotation relative to the
2102 observer, after both rotations and translations have been
2103 applied) is known as "billboarding".
2107 glTranslatef(f->y, 0, f->x); /* get matrix */
2109 glTranslatef (0, y_off, 0);
2111 glGetFloatv (GL_MODELVIEW_MATRIX, &m[0][0]); /* load rot. identity */
2112 m[0][0] = 1; m[1][0] = 0; m[2][0] = 0;
2113 m[0][1] = 0; m[1][1] = 1; m[2][1] = 0;
2114 m[0][2] = 0; m[1][2] = 0; m[2][2] = 1;
2115 glLoadIdentity(); /* reset modelview */
2116 glMultMatrixf (&m[0][0]); /* replace with ours */
2118 glTranslatef (0, 0, 0.1); /* move toward camera */
2120 glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */
2124 int cw, ch, w, h, ascent, descent;
2126 GLfloat max = 24; /* max point size to avoid pixellated text */
2128 /* Let the font be much larger on iPhone */
2129 if (mi->xgwa.height <= 640 || mi->xgwa.width <= 640)
2132 texture_string_metrics (bp->font_data, "X", &e, &ascent, &descent);
2134 ch = ascent + descent;
2136 if (ch > max) s *= max/ch;
2140 texture_string_metrics (bp->font_data, label, &e, 0, 0);
2142 h = e.ascent + e.descent;
2145 glTranslatef (-w/2, h*2/3 + (cw * 7), 0);
2148 glTranslatef (0, -h + (ch * 1.2), -0.1);
2149 draw_bubble_box (mi, w, h,
2150 ch * 2, /* corner radius */
2151 ch * 2.5, /* arrow height */
2152 w / 2 - cw * 8, /* arrow x */
2153 bp->text_bd, bp->text_bg);
2156 glColor4fv (bp->text_color);
2157 glTranslatef (0, ch/2, 0);
2158 print_texture_string (bp->font_data, label);
2163 /* More efficient to always call glEnable() with correct values
2164 than to call glPushAttrib()/glPopAttrib(), since reading
2165 attributes from GL does a round-trip and stalls the pipeline.
2168 glEnable (GL_LIGHTING);
2173 fill_words (ModeInfo *mi)
2175 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2176 char *p = bp->words + strlen(bp->words);
2179 int max = bp->max_lines;
2181 /* Fewer lines on iPhone */
2182 if ((mi->xgwa.height <= 640 || mi->xgwa.width <= 640) &&
2186 for (c = bp->words; c < p; c++)
2190 while (p < bp->words + sizeof(bp->words) - 1 &&
2193 int c = textclient_getc (bp->tc);
2208 bubble (ModeInfo *mi)
2210 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2212 GLfloat fade = 0.015;
2213 int chance = (talk_chance <= 0.0 ? 0 :
2214 talk_chance >= 0.99 ? 1 :
2215 (1-talk_chance) * 1000);
2217 char *s0 = strdup (bp->words);
2221 while (*s == '\n') s++;
2223 while (L > 0 && (s[L-1] == '\n' || s[L-1] == ' ' || s[L-1] == '\t'))
2225 if (! *s) goto DONE;
2228 if (debug_p) goto DONE;
2231 if (chance <= 0) goto DONE;
2233 if (bp->bubble_tick > 0)
2236 if (! bp->bubble_tick)
2240 if (! bp->bubble_tick)
2242 if (!(random() % chance))
2243 bp->bubble_tick = duration;
2248 scale = (bp->bubble_tick < duration * fade
2249 ? bp->bubble_tick / (duration * fade)
2250 : (bp->bubble_tick > duration * (1 - fade)
2251 ? 1 - ((bp->bubble_tick - duration * (1 - fade))
2252 / (duration * fade))
2255 draw_label (mi, &bp->walkers[0], 1.5, scale, s);
2260 #endif /* WORDBUBBLES */
2270 cmp_depth_sorter (const void *aa, const void *bb)
2272 const depth_sorter *a = (depth_sorter *) aa;
2273 const depth_sorter *b = (depth_sorter *) bb;
2274 return (a->d == b->d ? 0 : a->d < b->d ? -1 : 1);
2279 draw_robot (ModeInfo *mi)
2281 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2282 Display *dpy = MI_DISPLAY(mi);
2283 Window window = MI_WINDOW(mi);
2285 depth_sorter *sorted;
2288 if (!bp->glx_context)
2291 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
2293 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2299 int rot = current_device_rotation();
2301 if (rot == 180 || rot == -180) /* so much WTF */
2302 glRotatef (-68, 1, 0, 0);
2303 else if (rot == 90 || rot == -270)
2304 glRotatef (68, 0, 1, 0);
2305 else if (rot == -90 || rot == 270)
2306 glRotatef (-68, 0, 1, 0);
2308 glRotatef (rot, 0, 0, 1); /* right side up */
2312 gltrackball_rotate (bp->user_trackball);
2314 robot_size = size * 7;
2315 glScalef (robot_size, robot_size, robot_size);
2316 look_at_center (mi);
2319 glScalef (1/robot_size, 1/robot_size, 1/robot_size);
2320 glCallList (bp->dlists[GROUND]);
2331 glTranslatef (0, -1.2, 0);
2333 glRotatef (-43.5, 0, 0, 1);
2334 glRotatef (-90, 0, 1, 0);
2336 /* glTranslatef (bp->debug_z, bp->debug_y, 0); */
2339 if (!MI_IS_WIREFRAME(mi)) glDisable(GL_LIGHTING);
2340 if (!MI_IS_WIREFRAME(mi) && do_texture) glDisable(GL_TEXTURE_2D);
2343 glVertex3f(-10, 0, 0); glVertex3f(10, 0, 0);
2344 glVertex3f(0, -10, 0); glVertex3f(0, 10, 0);
2345 glVertex3f(0, 0, -10); glVertex3f(0, 0, 10);
2348 glTranslatef (-0.5, 0, -0.5);
2350 glColor3f (1, 0, 0);
2351 glBegin (GL_LINE_LOOP);
2352 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2353 glVertex3f (0, 1, 1); glVertex3f (0, 1, 0);
2356 glBegin (GL_LINE_LOOP);
2357 glVertex3f (1, 0, 0); glVertex3f (1, 0, 1);
2358 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2362 glColor3f (0.5, 0.5, 0.5);
2363 glFrontFace (GL_CCW);
2365 /* glVertex3f (0, 1, 0); glVertex3f (0, 1, 1); */
2366 /* glVertex3f (1, 1, 1); glVertex3f (1, 1, 0); */
2367 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2368 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2371 glFrontFace (GL_CW);
2373 glVertex3f (0, 1, 0); glVertex3f (0, 1, 1);
2374 glVertex3f (1, 1, 1); glVertex3f (1, 1, 0);
2375 glVertex3f (0, 0, 0); glVertex3f (0, 0, 1);
2376 glVertex3f (1, 0, 1); glVertex3f (1, 0, 0);
2380 glColor3f (1, 0, 0);
2382 glVertex3f (0, 0, 0); glVertex3f (1, 0, 0);
2383 glVertex3f (0, 0, 1); glVertex3f (1, 0, 1);
2384 glVertex3f (0, 1, 0); glVertex3f (1, 1, 0);
2385 glVertex3f (0, 1, 1); glVertex3f (1, 1, 1);
2387 glVertex3f (0, 0, 0); glVertex3f (1, 0, 1);
2388 glVertex3f (0, 0, 1); glVertex3f (1, 0, 0);
2389 glVertex3f (0, 1, 0); glVertex3f (1, 1, 1);
2390 glVertex3f (0, 1, 1); glVertex3f (1, 1, 0);
2393 if (!MI_IS_WIREFRAME(mi)) glEnable(GL_LIGHTING);
2394 if (!MI_IS_WIREFRAME(mi) && do_texture) glEnable(GL_TEXTURE_2D);
2401 /* For transparency to work right, we have to draw the robots from
2402 back to front, from the perspective of the observer. So project
2403 the origin of the robot to screen coordinates, and sort that by Z.
2405 sorted = (depth_sorter *) calloc (bp->nwalkers, sizeof (*sorted));
2407 GLdouble m[16], p[16];
2409 glGetDoublev (GL_MODELVIEW_MATRIX, m);
2410 glGetDoublev (GL_PROJECTION_MATRIX, p);
2411 glGetIntegerv (GL_VIEWPORT, v);
2413 for (i = 0; i < bp->nwalkers; i++)
2416 walker *f = &bp->walkers[i];
2417 gluProject (f->y, f->z, f->x, m, p, v, &x, &y, &z);
2421 qsort (sorted, i, sizeof(*sorted), cmp_depth_sorter);
2425 mi->polygon_count = 0;
2426 for (i = 0; i < bp->nwalkers; i++)
2428 int ii = sorted[i].i;
2429 walker *f = &bp->walkers[ii];
2430 int ticks = 22 * speed * f->speed;
2435 if (ticks < 1) ticks = 1;
2436 if (ticks > max) ticks = max;
2440 sprintf (tag, "%.4f, %.4f, %.4f",
2441 bp->debug_x, bp->debug_y, bp->debug_z);
2445 /* sprintf (tag + strlen(tag), " %d\n", ii); */
2446 sprintf (tag + strlen(tag), " %d\n", bp->nwalkers - i - 1);
2447 /* sprintf (tag + strlen(tag), "%.03f\n", sorted[i].d); */
2453 mi->polygon_count += draw_walker (mi, f, tag);
2455 for (ii = 0; ii < ticks; ii++)
2456 tick_walker (mi, f);
2463 if (mi->fps_p) do_fps (mi);
2466 glXSwapBuffers(dpy, window);
2470 release_robot (ModeInfo *mi)
2473 robot_configuration *bp = &bps[MI_SCREEN(mi)];
2474 textclient_close (bp->tc);
2478 XSCREENSAVER_MODULE_2 ("WindupRobot", winduprobot, robot)