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" \
23 "*showFPS: False \n" \
25 "*wireframe: False \n" \
30 # define refresh_cube 0
31 # define release_cube 0
32 #define DEF_SPEED "1.0"
33 #define DEF_SPIN "False"
34 #define DEF_WANDER "False"
37 #define countof(x) (sizeof((x))/sizeof((*x)))
40 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
42 #define RANDSIGN() ((random() & 1) ? 1 : -1)
44 #include "xlockmore.h"
46 #include "gltrackball.h"
47 #include "xpm-ximage.h"
50 #ifdef USE_GL /* whole file */
54 extern const struct gllist *companion_quad, *companion_disc, *companion_heart;
55 static const struct gllist * const *all_objs[] = {
56 &companion_quad, &companion_disc, &companion_heart
63 #define SPEED_SCALE 0.2
69 GLfloat ddx, ddy, ddz;
76 GLXContext *glx_context;
77 trackball_state *trackball;
88 static cube_configuration *bps = NULL;
92 static Bool do_wander;
94 static XrmOptionDescRec opts[] = {
95 { "-speed", ".speed", XrmoptionSepArg, 0 },
96 { "-spin", ".spin", XrmoptionNoArg, "True" },
97 { "+spin", ".spin", XrmoptionNoArg, "False" },
98 { "-wander", ".wander", XrmoptionNoArg, "True" },
99 { "+wander", ".wander", XrmoptionNoArg, "False" },
102 static argtype vars[] = {
103 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
104 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
105 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
108 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
114 reset_floater (ModeInfo *mi, floater *f)
116 cube_configuration *bp = &bps[MI_SCREEN(mi)];
122 /* Yes, I know I'm varying the force of gravity instead of varying the
123 launch velocity. That's intentional: empirical studies indicate
124 that it's way, way funnier that way. */
130 /* -0.18 max -0.3 top -0.4 middle -0.6 bottom */
131 f->ddy = speed * SPEED_SCALE * (-0.6 + BELLRAND(0.45));
135 if (do_spin || do_wander)
138 f->spinner_p = !(random() % (3 * bp->nfloaters));
140 if (! (random() % (30 * bp->nfloaters)))
142 f->dx = BELLRAND(1.8) * RANDSIGN();
143 f->dz = BELLRAND(1.8) * RANDSIGN();
147 if (do_spin || do_wander)
150 if (bp->nfloaters > 2)
151 f->y += frand(3.0) * RANDSIGN();
157 tick_floater (ModeInfo *mi, floater *f)
159 cube_configuration *bp = &bps[MI_SCREEN(mi)];
161 if (bp->button_down_p) return;
163 if (do_spin || do_wander) return;
169 f->x += f->dx * speed * SPEED_SCALE;
170 f->y += f->dy * speed * SPEED_SCALE;
171 f->z += f->dz * speed * SPEED_SCALE;
173 if (f->y < -BOTTOM ||
174 f->x < -BOTTOM*8 || f->x > BOTTOM*8 ||
175 f->z < -BOTTOM*8 || f->z > BOTTOM*8)
176 reset_floater (mi, f);
184 build_corner (ModeInfo *mi)
186 cube_configuration *bp = &bps[MI_SCREEN(mi)];
188 const struct gllist *gll = *all_objs[BASE_QUAD];
191 glTranslatef (-0.5, -0.5, -0.5);
195 glRotatef (180, 0, 1, 0);
196 glRotatef (180, 0, 0, 1);
197 glTranslatef (-0.12, -1.64, 0.12);
198 glCallList (bp->dlists[BASE_QUAD]);
201 return gll->points / 3;
206 build_face (ModeInfo *mi)
209 cube_configuration *bp = &bps[MI_SCREEN(mi)];
210 int wire = MI_IS_WIREFRAME(mi);
212 const struct gllist *gll;
214 GLfloat base_color[4] = {0.53, 0.60, 0.66, 1.00};
215 GLfloat heart_color[4] = {0.92, 0.67, 1.00, 1.00};
216 GLfloat disc_color[4] = {0.75, 0.92, 1.00, 1.00};
217 GLfloat corner_color[4] = {0.75, 0.92, 1.00, 1.00};
222 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, base_color);
224 glNormal3f (0, 0, -1);
225 glTranslatef (-0.5, -0.5, -0.5);
228 glVertex3f (0, 0, 0);
229 glVertex3f (0, 0.5-w, 0);
230 glVertex3f (0.5-w, 0.5-w, 0);
231 glVertex3f (0.5-w, 0, 0);
233 glVertex3f (0.5+w, 0, 0);
234 glVertex3f (0.5+w, 0.5-w, 0);
235 glVertex3f (1, 0.5-w, 0);
236 glVertex3f (1, 0, 0);
238 glVertex3f (0, 0.5+w, 0);
239 glVertex3f (0, 1, 0);
240 glVertex3f (0.5-w, 1, 0);
241 glVertex3f (0.5-w, 0.5+w, 0);
243 glVertex3f (0.5+w, 0.5+w, 0);
244 glVertex3f (0.5+w, 1, 0);
245 glVertex3f (1, 1, 0);
246 glVertex3f (1, 0.5+w, 0);
249 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
251 glNormal3f (0, -1, 0);
253 glVertex3f (0, 0.5+w, 0);
254 glVertex3f (1, 0.5+w, 0);
255 glVertex3f (1, 0.5+w, w);
256 glVertex3f (0, 0.5+w, w);
259 glNormal3f (0, 1, 0);
261 glVertex3f (0, 0.5-w, w);
262 glVertex3f (1, 0.5-w, w);
263 glVertex3f (1, 0.5-w, 0);
264 glVertex3f (0, 0.5-w, 0);
267 glNormal3f (-1, 0, 0);
269 glVertex3f (0.5+w, 0, w);
270 glVertex3f (0.5+w, 1, w);
271 glVertex3f (0.5+w, 1, 0);
272 glVertex3f (0.5+w, 0, 0);
275 glNormal3f (1, 0, 0);
277 glVertex3f (0.5-w, 0, 0);
278 glVertex3f (0.5-w, 1, 0);
279 glVertex3f (0.5-w, 1, w);
280 glVertex3f (0.5-w, 0, w);
283 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
285 glNormal3f (0, 0, -1);
286 glTranslatef (0, 0, w);
288 glVertex3f (0, 0, 0);
289 glVertex3f (0, 1, 0);
290 glVertex3f (1, 1, 0);
291 glVertex3f (1, 0, 0);
297 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, corner_color);
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); glRotatef (90, 0, 0, 1);
303 polys += build_corner (mi);
305 glRotatef (90, 0, 0, 1);
306 glTranslatef (0.585, -0.585, -0.5655);
310 glRotatef (180, 0, 1, 0);
314 gll = *all_objs[BASE_HEART];
315 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, heart_color);
316 glCallList (bp->dlists[BASE_HEART]);
317 polys += gll->points / 3;
320 gll = *all_objs[BASE_DISC];
321 glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, disc_color);
322 glCallList (bp->dlists[BASE_DISC]);
323 polys += gll->points / 3;
331 build_cube (ModeInfo *mi)
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, 0, 1, 0);
338 polys += build_face (mi); glRotatef (90, 1, 0, 0);
339 polys += build_face (mi); glRotatef (180,1, 0, 0);
340 polys += build_face (mi);
346 /* Window management, etc
349 reshape_cube (ModeInfo *mi, int width, int height)
351 GLfloat h = (GLfloat) height / (GLfloat) width;
353 glViewport (0, 0, (GLint) width, (GLint) height);
355 glMatrixMode(GL_PROJECTION);
357 gluPerspective (30.0, 1/h, 1.0, 100);
359 glMatrixMode(GL_MODELVIEW);
361 gluLookAt( 0.0, 0.0, 30.0,
365 glClear(GL_COLOR_BUFFER_BIT);
370 cube_handle_event (ModeInfo *mi, XEvent *event)
372 cube_configuration *bp = &bps[MI_SCREEN(mi)];
374 if (event->xany.type == ButtonPress &&
375 event->xbutton.button == Button1)
377 bp->button_down_p = True;
378 gltrackball_start (bp->trackball,
379 event->xbutton.x, event->xbutton.y,
380 MI_WIDTH (mi), MI_HEIGHT (mi));
383 else if (event->xany.type == ButtonRelease &&
384 event->xbutton.button == Button1)
386 bp->button_down_p = False;
389 else if (event->xany.type == ButtonPress &&
390 (event->xbutton.button == Button4 ||
391 event->xbutton.button == Button5 ||
392 event->xbutton.button == Button6 ||
393 event->xbutton.button == Button7))
395 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 3,
396 !event->xbutton.state);
399 else if (event->xany.type == MotionNotify &&
402 gltrackball_track (bp->trackball,
403 event->xmotion.x, event->xmotion.y,
404 MI_WIDTH (mi), MI_HEIGHT (mi));
413 init_cube (ModeInfo *mi)
415 cube_configuration *bp;
416 int wire = MI_IS_WIREFRAME(mi);
420 bps = (cube_configuration *)
421 calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
423 fprintf(stderr, "%s: out of memory\n", progname);
428 bp = &bps[MI_SCREEN(mi)];
430 bp->glx_context = init_GL(mi);
432 reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
434 glShadeModel(GL_SMOOTH);
436 glEnable(GL_DEPTH_TEST);
437 glEnable(GL_NORMALIZE);
438 glEnable(GL_CULL_FACE);
442 GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
443 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
444 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
445 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
446 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
448 glEnable(GL_LIGHTING);
450 glEnable(GL_DEPTH_TEST);
451 glEnable(GL_CULL_FACE);
453 glLightfv(GL_LIGHT0, GL_POSITION, pos);
454 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
455 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
456 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
459 bp->trackball = gltrackball_init ();
461 bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
462 for (i = 0; i < countof(all_objs)+1; i++)
463 bp->dlists[i] = glGenLists (1);
465 for (i = 0; i < countof(all_objs); i++)
467 const struct gllist *gll = *all_objs[i];
468 glNewList (bp->dlists[i], GL_COMPILE);
469 renderList (gll, wire);
473 glNewList (bp->dlists[i], GL_COMPILE);
474 bp->cube_polys = build_cube (mi);
478 bp->nfloaters = MI_COUNT (mi);
479 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
481 for (i = 0; i < bp->nfloaters; i++)
483 floater *f = &bp->floaters[i];
484 double spin_speed = do_spin ? 0.7 : 10;
485 double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
486 double spin_accel = 0.5;
487 f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
491 if (bp->nfloaters == 2)
497 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
506 reset_floater (mi, f);
512 draw_floater (ModeInfo *mi, floater *f)
514 cube_configuration *bp = &bps[MI_SCREEN(mi)];
518 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
521 glTranslatef (f->x, f->y, f->z);
524 glTranslatef (x, y, z);
527 get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
529 if (do_spin || f->spinner_p)
531 glRotatef (x * 360, 1, 0, 0);
532 glRotatef (y * 360, 0, 1, 0);
533 glRotatef (z * 360, 0, 0, 1);
537 glRotatef (f->zr * 360, 0, 1, 0);
541 if (bp->nfloaters > 99) n *= 0.05;
542 else if (bp->nfloaters > 25) n *= 0.18;
543 else if (bp->nfloaters > 9) n *= 0.3;
544 else if (bp->nfloaters > 1) n *= 0.7;
548 if ((do_spin || do_wander) && bp->nfloaters > 1)
553 glCallList (bp->dlists[FULL_CUBE]);
554 mi->polygon_count += bp->cube_polys;
555 /* build_cube (mi);*/
563 draw_cube (ModeInfo *mi)
565 cube_configuration *bp = &bps[MI_SCREEN(mi)];
566 Display *dpy = MI_DISPLAY(mi);
567 Window window = MI_WINDOW(mi);
570 if (!bp->glx_context)
573 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
575 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
578 glRotatef(current_device_rotation(), 0, 0, 1);
579 gltrackball_rotate (bp->trackball);
583 mi->polygon_count = 0;
589 F.dx = F.dy = F.dz = 0;
590 F.ddx = F.ddy = F.ddz = 0;
591 F.rot = make_rotator (0, 0, 0, 1, 0, False);
592 glRotatef (45, 0, 1, 0);
593 draw_floater (mi, &F);
596 for (i = 0; i < bp->nfloaters; i++)
598 floater *f = &bp->floaters[i];
599 draw_floater (mi, f);
600 tick_floater (mi, f);
606 if (mi->fps_p) do_fps (mi);
609 glXSwapBuffers(dpy, window);
612 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)