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" \
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 (gltrackball_event_handler (event, bp->trackball,
374 MI_WIDTH (mi), MI_HEIGHT (mi),
383 init_cube (ModeInfo *mi)
385 cube_configuration *bp;
386 int wire = MI_IS_WIREFRAME(mi);
390 bps = (cube_configuration *)
391 calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
393 fprintf(stderr, "%s: out of memory\n", progname);
398 bp = &bps[MI_SCREEN(mi)];
400 bp->glx_context = init_GL(mi);
402 reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
404 glShadeModel(GL_SMOOTH);
406 glEnable(GL_DEPTH_TEST);
407 glEnable(GL_NORMALIZE);
408 glEnable(GL_CULL_FACE);
412 GLfloat pos[4] = {0.7, 0.2, 0.4, 0.0};
413 /* GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};*/
414 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
415 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
416 GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
418 glEnable(GL_LIGHTING);
420 glEnable(GL_DEPTH_TEST);
421 glEnable(GL_CULL_FACE);
423 glLightfv(GL_LIGHT0, GL_POSITION, pos);
424 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
425 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
426 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
429 bp->trackball = gltrackball_init (False);
431 bp->dlists = (GLuint *) calloc (countof(all_objs)+2, sizeof(GLuint));
432 for (i = 0; i < countof(all_objs)+1; i++)
433 bp->dlists[i] = glGenLists (1);
435 for (i = 0; i < countof(all_objs); i++)
437 const struct gllist *gll = *all_objs[i];
438 glNewList (bp->dlists[i], GL_COMPILE);
439 renderList (gll, wire);
443 glNewList (bp->dlists[i], GL_COMPILE);
444 bp->cube_polys = build_cube (mi);
448 bp->nfloaters = MI_COUNT (mi);
449 bp->floaters = (floater *) calloc (bp->nfloaters, sizeof (floater));
451 for (i = 0; i < bp->nfloaters; i++)
453 floater *f = &bp->floaters[i];
454 double spin_speed = do_spin ? 0.7 : 10;
455 double wander_speed = do_wander ? 0.02 : 0.05 * speed * SPEED_SCALE;
456 double spin_accel = 0.5;
457 f->rot = make_rotator (spin_speed, spin_speed, spin_speed,
461 if (bp->nfloaters == 2)
467 double th = (i - 1) * M_PI*2 / (bp->nfloaters-1);
476 reset_floater (mi, f);
482 draw_floater (ModeInfo *mi, floater *f)
484 cube_configuration *bp = &bps[MI_SCREEN(mi)];
488 get_position (f->rot, &x, &y, &z, !bp->button_down_p);
491 glTranslatef (f->x, f->y, f->z);
494 glTranslatef (x, y, z);
497 get_rotation (f->rot, &x, &y, &z, !bp->button_down_p);
499 if (do_spin || f->spinner_p)
501 glRotatef (x * 360, 1, 0, 0);
502 glRotatef (y * 360, 0, 1, 0);
503 glRotatef (z * 360, 0, 0, 1);
507 glRotatef (f->zr * 360, 0, 1, 0);
511 if (bp->nfloaters > 99) n *= 0.05;
512 else if (bp->nfloaters > 25) n *= 0.18;
513 else if (bp->nfloaters > 9) n *= 0.3;
514 else if (bp->nfloaters > 1) n *= 0.7;
518 if ((do_spin || do_wander) && bp->nfloaters > 1)
523 glCallList (bp->dlists[FULL_CUBE]);
524 mi->polygon_count += bp->cube_polys;
525 /* build_cube (mi);*/
533 draw_cube (ModeInfo *mi)
535 cube_configuration *bp = &bps[MI_SCREEN(mi)];
536 Display *dpy = MI_DISPLAY(mi);
537 Window window = MI_WINDOW(mi);
540 if (!bp->glx_context)
543 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
545 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
548 glRotatef(current_device_rotation(), 0, 0, 1);
549 gltrackball_rotate (bp->trackball);
553 mi->polygon_count = 0;
559 F.dx = F.dy = F.dz = 0;
560 F.ddx = F.ddy = F.ddz = 0;
561 F.rot = make_rotator (0, 0, 0, 1, 0, False);
562 glRotatef (45, 0, 1, 0);
563 draw_floater (mi, &F);
566 for (i = 0; i < bp->nfloaters; i++)
568 floater *f = &bp->floaters[i];
569 draw_floater (mi, f);
570 tick_floater (mi, f);
576 if (mi->fps_p) do_fps (mi);
579 glXSwapBuffers(dpy, window);
582 XSCREENSAVER_MODULE_2 ("CompanionCube", companioncube, cube)