1 /* companioncube, Copyright (c) 2011 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
12 /* The symptoms most commonly produced by Enrichment Center testing are
13 superstition, perceiving inanimate objects as alive, and hallucinations.
14 The Enrichment Center reminds you that the weighted companion cube will
15 never threaten to stab you and, in fact, cannot speak. In the event that
16 the Weighted Companion Cube does speak, the Enrichment Center urges you to
21 #define DEFAULTS "*delay: 30000 \n" \
22 "*showFPS: False \n" \
24 "*wireframe: False \n" \
29 # define refresh_cube 0
30 # define release_cube 0
31 #define DEF_SPEED "1.0"
32 #define DEF_SPIN "False"
33 #define DEF_WANDER "False"
36 #define countof(x) (sizeof((x))/sizeof((*x)))
39 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
41 #define RANDSIGN() ((random() & 1) ? 1 : -1)
43 #include "xlockmore.h"
45 #include "gltrackball.h"
46 #include "xpm-ximage.h"
49 #ifdef USE_GL /* whole file */
53 extern const struct gllist *companion_quad, *companion_disc, *companion_heart;
54 static const struct gllist * const *all_objs[] = {
55 &companion_quad, &companion_disc, &companion_heart
62 #define SPEED_SCALE 0.2
68 GLfloat ddx, ddy, ddz;
75 GLXContext *glx_context;
76 trackball_state *trackball;
87 static cube_configuration *bps = NULL;
91 static Bool do_wander;
93 static XrmOptionDescRec opts[] = {
94 { "-speed", ".speed", XrmoptionSepArg, 0 },
95 { "-spin", ".spin", XrmoptionNoArg, "True" },
96 { "+spin", ".spin", XrmoptionNoArg, "False" },
97 { "-wander", ".wander", XrmoptionNoArg, "True" },
98 { "+wander", ".wander", XrmoptionNoArg, "False" },
101 static argtype vars[] = {
102 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
103 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
104 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
107 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
113 reset_floater (ModeInfo *mi, floater *f)
115 cube_configuration *bp = &bps[MI_SCREEN(mi)];
121 /* Yes, I know I'm varying the force of gravity instead of varying the
122 launch velocity. That's intentional: empirical studies indicate
123 that it's way, way funnier that way. */
129 /* -0.18 max -0.3 top -0.4 middle -0.6 bottom */
130 f->ddy = speed * SPEED_SCALE * (-0.6 + BELLRAND(0.45));
134 if (do_spin || do_wander)
137 f->spinner_p = !(random() % (3 * bp->nfloaters));
139 if (! (random() % (30 * bp->nfloaters)))
141 f->dx = BELLRAND(1.8) * RANDSIGN();
142 f->dz = BELLRAND(1.8) * RANDSIGN();
146 if (do_spin || do_wander)
149 if (bp->nfloaters > 2)
150 f->y += frand(3.0) * RANDSIGN();
156 tick_floater (ModeInfo *mi, floater *f)
158 cube_configuration *bp = &bps[MI_SCREEN(mi)];
160 if (bp->button_down_p) return;
162 if (do_spin || do_wander) return;
168 f->x += f->dx * speed * SPEED_SCALE;
169 f->y += f->dy * speed * SPEED_SCALE;
170 f->z += f->dz * speed * SPEED_SCALE;
172 if (f->y < -BOTTOM ||
173 f->x < -BOTTOM*8 || f->x > BOTTOM*8 ||
174 f->z < -BOTTOM*8 || f->z > BOTTOM*8)
175 reset_floater (mi, f);
183 build_corner (ModeInfo *mi)
185 cube_configuration *bp = &bps[MI_SCREEN(mi)];
187 const struct gllist *gll = *all_objs[BASE_QUAD];
190 glTranslatef (-0.5, -0.5, -0.5);
194 glRotatef (180, 0, 1, 0);
195 glRotatef (180, 0, 0, 1);
196 glTranslatef (-0.12, -1.64, 0.12);
197 glCallList (bp->dlists[BASE_QUAD]);
200 return gll->points / 3;
205 build_face (ModeInfo *mi)
208 cube_configuration *bp = &bps[MI_SCREEN(mi)];
209 int wire = MI_IS_WIREFRAME(mi);
211 const struct gllist *gll;
213 GLfloat base_color[4] = {0.53, 0.60, 0.66, 1.00};
214 GLfloat heart_color[4] = {0.92, 0.67, 1.00, 1.00};
215 GLfloat disc_color[4] = {0.75, 0.92, 1.00, 1.00};
216 GLfloat corner_color[4] = {0.75, 0.92, 1.00, 1.00};
221 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, base_color);
223 glNormal3f (0, 0, -1);
224 glTranslatef (-0.5, -0.5, -0.5);
227 glVertex3f (0, 0, 0);
228 glVertex3f (0, 0.5-w, 0);
229 glVertex3f (0.5-w, 0.5-w, 0);
230 glVertex3f (0.5-w, 0, 0);
232 glVertex3f (0.5+w, 0, 0);
233 glVertex3f (0.5+w, 0.5-w, 0);
234 glVertex3f (1, 0.5-w, 0);
235 glVertex3f (1, 0, 0);
237 glVertex3f (0, 0.5+w, 0);
238 glVertex3f (0, 1, 0);
239 glVertex3f (0.5-w, 1, 0);
240 glVertex3f (0.5-w, 0.5+w, 0);
242 glVertex3f (0.5+w, 0.5+w, 0);
243 glVertex3f (0.5+w, 1, 0);
244 glVertex3f (1, 1, 0);
245 glVertex3f (1, 0.5+w, 0);
248 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
250 glNormal3f (0, -1, 0);
252 glVertex3f (0, 0.5+w, 0);
253 glVertex3f (1, 0.5+w, 0);
254 glVertex3f (1, 0.5+w, w);
255 glVertex3f (0, 0.5+w, w);
258 glNormal3f (0, 1, 0);
260 glVertex3f (0, 0.5-w, w);
261 glVertex3f (1, 0.5-w, w);
262 glVertex3f (1, 0.5-w, 0);
263 glVertex3f (0, 0.5-w, 0);
266 glNormal3f (-1, 0, 0);
268 glVertex3f (0.5+w, 0, w);
269 glVertex3f (0.5+w, 1, w);
270 glVertex3f (0.5+w, 1, 0);
271 glVertex3f (0.5+w, 0, 0);
274 glNormal3f (1, 0, 0);
276 glVertex3f (0.5-w, 0, 0);
277 glVertex3f (0.5-w, 1, 0);
278 glVertex3f (0.5-w, 1, w);
279 glVertex3f (0.5-w, 0, w);
282 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
284 glNormal3f (0, 0, -1);
285 glTranslatef (0, 0, w);
287 glVertex3f (0, 0, 0);
288 glVertex3f (0, 1, 0);
289 glVertex3f (1, 1, 0);
290 glVertex3f (1, 0, 0);
296 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, corner_color);
299 polys += build_corner (mi); glRotatef (90, 0, 0, 1);
300 polys += build_corner (mi); glRotatef (90, 0, 0, 1);
301 polys += build_corner (mi); glRotatef (90, 0, 0, 1);
302 polys += build_corner (mi);
304 glRotatef (90, 0, 0, 1);
305 glTranslatef (0.585, -0.585, -0.5655);
309 glRotatef (180, 0, 1, 0);
313 gll = *all_objs[BASE_HEART];
314 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
315 glCallList (bp->dlists[BASE_HEART]);
316 polys += gll->points / 3;
319 gll = *all_objs[BASE_DISC];
320 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, disc_color);
321 glCallList (bp->dlists[BASE_DISC]);
322 polys += gll->points / 3;
330 build_cube (ModeInfo *mi)
334 polys += build_face (mi); glRotatef (90, 0, 1, 0);
335 polys += build_face (mi); glRotatef (90, 0, 1, 0);
336 polys += build_face (mi); glRotatef (90, 0, 1, 0);
337 polys += build_face (mi); glRotatef (90, 1, 0, 0);
338 polys += build_face (mi); glRotatef (180,1, 0, 0);
339 polys += build_face (mi);
345 /* Window management, etc
348 reshape_cube (ModeInfo *mi, int width, int height)
350 GLfloat h = (GLfloat) height / (GLfloat) width;
352 glViewport (0, 0, (GLint) width, (GLint) height);
354 glMatrixMode(GL_PROJECTION);
356 gluPerspective (30.0, 1/h, 1.0, 100);
358 glMatrixMode(GL_MODELVIEW);
360 gluLookAt( 0.0, 0.0, 30.0,
364 glClear(GL_COLOR_BUFFER_BIT);
369 cube_handle_event (ModeInfo *mi, XEvent *event)
371 cube_configuration *bp = &bps[MI_SCREEN(mi)];
373 if (event->xany.type == ButtonPress &&
374 event->xbutton.button == Button1)
376 bp->button_down_p = True;
377 gltrackball_start (bp->trackball,
378 event->xbutton.x, event->xbutton.y,
379 MI_WIDTH (mi), MI_HEIGHT (mi));
382 else if (event->xany.type == ButtonRelease &&
383 event->xbutton.button == Button1)
385 bp->button_down_p = False;
388 else if (event->xany.type == ButtonPress &&
389 (event->xbutton.button == Button4 ||
390 event->xbutton.button == Button5 ||
391 event->xbutton.button == Button6 ||
392 event->xbutton.button == Button7))
394 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 3,
395 !event->xbutton.state);
398 else if (event->xany.type == MotionNotify &&
401 gltrackball_track (bp->trackball,
402 event->xmotion.x, event->xmotion.y,
403 MI_WIDTH (mi), MI_HEIGHT (mi));
412 init_cube (ModeInfo *mi)
414 cube_configuration *bp;
415 int wire = MI_IS_WIREFRAME(mi);
419 bps = (cube_configuration *)
420 calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
422 fprintf(stderr, "%s: out of memory\n", progname);
427 bp = &bps[MI_SCREEN(mi)];
429 bp->glx_context = init_GL(mi);
431 reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
433 glShadeModel(GL_SMOOTH);
435 glEnable(GL_DEPTH_TEST);
436 glEnable(GL_NORMALIZE);
437 glEnable(GL_CULL_FACE);
441 GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
442 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
443 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
444 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
445 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
447 glEnable(GL_LIGHTING);
449 glEnable(GL_DEPTH_TEST);
450 glEnable(GL_CULL_FACE);
452 glLightfv(GL_LIGHT0, GL_POSITION, pos);
453 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
454 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
455 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
458 bp->trackball = gltrackball_init ();
460 bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
461 for (i = 0; i < countof(all_objs)+1; i++)
462 bp->dlists[i] = glGenLists (1);
464 for (i = 0; i < countof(all_objs); i++)
466 const struct gllist *gll = *all_objs[i];
467 glNewList (bp->dlists[i], GL_COMPILE);
468 renderList (gll, wire);
472 glNewList (bp->dlists[i], GL_COMPILE);
473 bp->cube_polys = build_cube (mi);
477 bp->nfloaters = MI_COUNT (mi);
478 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
480 for (i = 0; i < bp->nfloaters; i++)
482 floater *f = &bp->floaters[i];
483 double spin_speed = do_spin ? 0.7 : 10;
484 double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
485 double spin_accel = 0.5;
486 f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
490 if (bp->nfloaters == 2)
496 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
505 reset_floater (mi, f);
511 draw_floater (ModeInfo *mi, floater *f)
513 cube_configuration *bp = &bps[MI_SCREEN(mi)];
517 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
520 glTranslatef (f->x, f->y, f->z);
523 glTranslatef (x, y, z);
526 get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
528 if (do_spin || f->spinner_p)
530 glRotatef (x * 360, 1, 0, 0);
531 glRotatef (y * 360, 0, 1, 0);
532 glRotatef (z * 360, 0, 0, 1);
536 glRotatef (f->zr * 360, 0, 1, 0);
540 if (bp->nfloaters > 99) n *= 0.05;
541 else if (bp->nfloaters > 25) n *= 0.18;
542 else if (bp->nfloaters > 9) n *= 0.3;
543 else if (bp->nfloaters > 1) n *= 0.7;
547 if ((do_spin || do_wander) && bp->nfloaters > 1)
552 glCallList (bp->dlists[FULL_CUBE]);
553 mi->polygon_count += bp->cube_polys;
554 /* build_cube (mi);*/
562 draw_cube (ModeInfo *mi)
564 cube_configuration *bp = &bps[MI_SCREEN(mi)];
565 Display *dpy = MI_DISPLAY(mi);
566 Window window = MI_WINDOW(mi);
569 if (!bp->glx_context)
572 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
574 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
577 glRotatef(current_device_rotation(), 0, 0, 1);
578 gltrackball_rotate (bp->trackball);
582 mi->polygon_count = 0;
588 F.dx = F.dy = F.dz = 0;
589 F.ddx = F.ddy = F.ddz = 0;
590 F.rot = make_rotator (0, 0, 0, 1, 0, False);
591 glRotatef (45, 0, 1, 0);
592 draw_floater (mi, &F);
595 for (i = 0; i < bp->nfloaters; i++)
597 floater *f = &bp->floaters[i];
598 draw_floater (mi, f);
599 tick_floater (mi, f);
605 if (mi->fps_p) do_fps (mi);
608 glXSwapBuffers(dpy, window);
611 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)