1 /* companioncube, Copyright (c) 2011-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
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" \
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;
353 if (width > height * 5) { /* tiny window: show middle */
354 height = width * 9/16;
356 h = height / (GLfloat) width;
359 glViewport (0, y, (GLint) width, (GLint) height);
361 glMatrixMode(GL_PROJECTION);
363 gluPerspective (30.0, 1/h, 1.0, 100);
365 glMatrixMode(GL_MODELVIEW);
367 gluLookAt( 0.0, 0.0, 30.0,
371 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
373 int o = (int) current_device_rotation();
374 if (o != 0 && o != 180 && o != -180)
375 glScalef (1/h, 1/h, 1/h);
379 glClear(GL_COLOR_BUFFER_BIT);
384 cube_handle_event (ModeInfo *mi, XEvent *event)
386 cube_configuration *bp = &bps[MI_SCREEN(mi)];
388 if (gltrackball_event_handler (event, bp->trackball,
389 MI_WIDTH (mi), MI_HEIGHT (mi),
398 init_cube (ModeInfo *mi)
400 cube_configuration *bp;
401 int wire = MI_IS_WIREFRAME(mi);
406 bp = &bps[MI_SCREEN(mi)];
408 bp->glx_context = init_GL(mi);
410 reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
412 glShadeModel(GL_SMOOTH);
414 glEnable(GL_DEPTH_TEST);
415 glEnable(GL_NORMALIZE);
416 glEnable(GL_CULL_FACE);
420 GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
421 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
422 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
423 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
424 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
426 glEnable(GL_LIGHTING);
428 glEnable(GL_DEPTH_TEST);
429 glEnable(GL_CULL_FACE);
431 glLightfv(GL_LIGHT0, GL_POSITION, pos);
432 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
433 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
434 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
437 bp->trackball = gltrackball_init (False);
439 bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
440 for (i = 0; i < countof(all_objs)+1; i++)
441 bp->dlists[i] = glGenLists (1);
443 for (i = 0; i < countof(all_objs); i++)
445 const struct gllist *gll = *all_objs[i];
446 glNewList (bp->dlists[i], GL_COMPILE);
447 renderList (gll, wire);
451 glNewList (bp->dlists[i], GL_COMPILE);
452 bp->cube_polys = build_cube (mi);
456 bp->nfloaters = MI_COUNT (mi);
457 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
459 for (i = 0; i < bp->nfloaters; i++)
461 floater *f = &bp->floaters[i];
462 double spin_speed = do_spin ? 0.7 : 10;
463 double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
464 double spin_accel = 0.5;
465 f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
469 if (bp->nfloaters == 2)
475 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
484 reset_floater (mi, f);
490 draw_floater (ModeInfo *mi, floater *f)
492 cube_configuration *bp = &bps[MI_SCREEN(mi)];
496 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
499 glTranslatef (f->x, f->y, f->z);
502 glTranslatef (x, y, z);
505 get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
507 if (do_spin || f->spinner_p)
509 glRotatef (x * 360, 1, 0, 0);
510 glRotatef (y * 360, 0, 1, 0);
511 glRotatef (z * 360, 0, 0, 1);
515 glRotatef (f->zr * 360, 0, 1, 0);
519 if (bp->nfloaters > 99) n *= 0.05;
520 else if (bp->nfloaters > 25) n *= 0.18;
521 else if (bp->nfloaters > 9) n *= 0.3;
522 else if (bp->nfloaters > 1) n *= 0.7;
526 if ((do_spin || do_wander) && bp->nfloaters > 1)
531 glCallList (bp->dlists[FULL_CUBE]);
532 mi->polygon_count += bp->cube_polys;
533 /* build_cube (mi);*/
541 draw_cube (ModeInfo *mi)
543 cube_configuration *bp = &bps[MI_SCREEN(mi)];
544 Display *dpy = MI_DISPLAY(mi);
545 Window window = MI_WINDOW(mi);
548 if (!bp->glx_context)
551 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
553 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
556 glRotatef(current_device_rotation(), 0, 0, 1);
557 gltrackball_rotate (bp->trackball);
561 mi->polygon_count = 0;
567 F.dx = F.dy = F.dz = 0;
568 F.ddx = F.ddy = F.ddz = 0;
569 F.rot = make_rotator (0, 0, 0, 1, 0, False);
570 glRotatef (45, 0, 1, 0);
571 draw_floater (mi, &F);
574 for (i = 0; i < bp->nfloaters; i++)
576 floater *f = &bp->floaters[i];
577 draw_floater (mi, f);
578 tick_floater (mi, f);
584 if (mi->fps_p) do_fps (mi);
587 glXSwapBuffers(dpy, window);
590 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)