1 /* topblock, Copyright (c) 2006-2008
2 * rednuht <topblock.xscreensaver@jumpstation.co.uk>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
15 topBlock - a simple openGL 3D hack of falling blocks
16 based on jwz's dangerball hack
18 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
20 information on this hack might be found at
21 http://www.jumpstation.co.uk/xscreensaver/topblock/
24 25/02/2006 v1.0 release
25 29/04/2006 v1.11 updated to better fit with xscreensaver v5
26 colors defaults to 7 (no black)
27 19/06/2006 v1.2 fixed dropSpeed = 7 bug, added gltrackball support and some code neatening, thanks to Valdis Kletnieks and JWZ for their input.
32 # define refresh_topBlock 0
34 #define DEFAULTS "*delay: 10000 \n" \
36 "*showFPS: False \n" \
37 "*wireframe: False \n" \
40 #define countof(x) (sizeof((x))/sizeof((*x)))
42 #include "xlockmore.h"
45 #include "gltrackball.h"
48 #ifdef USE_GL /* whole file */
51 # include <OpenGL/glu.h>
58 GLXContext *glx_context;
59 trackball_state *trackball;
62 GLfloat highest,highestFalling;
63 GLfloat eyeLine,eyeX,eyeY,eyeZ;
64 int carpetWidth, carpetLength;
66 GLfloat followRadius,followAngle;
71 NODE *blockNodeFollow;
93 static XrmOptionDescRec opts[] = {
94 { "-size", ".size", XrmoptionSepArg, 0 },
95 { "-spawn", ".spawn", XrmoptionSepArg, 0 },
96 { "-camX", ".camX", XrmoptionSepArg, 0 },
97 { "-camY", ".camY", XrmoptionSepArg, 0 },
98 { "-camZ", ".camZ", XrmoptionSepArg, 0 },
99 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
100 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
101 { "+carpet", ".carpet", XrmoptionNoArg, "False" },
102 { "+nipples", ".nipples", XrmoptionNoArg, "False" },
103 { "-blob", ".blob", XrmoptionNoArg, "True" },
104 { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
105 { "-follow", ".follow", XrmoptionNoArg, "True" },
106 { "-maxFalling", ".maxFalling", XrmoptionSepArg, 0 },
107 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
108 { "-maxColors", ".maxColors", XrmoptionSepArg, 0 },
109 { "-dropSpeed", ".dropSpeed", XrmoptionSepArg, 0 },
110 { "-override", ".override", XrmoptionNoArg, "True" },
113 #define DEF_override "False"
114 #define DEF_rotate "True"
115 #define DEF_follow "False"
116 #define DEF_drawCarpet "True"
117 #define DEF_drawBlob "False"
118 #define DEF_drawNipples "True"
119 #define DEF_rotateSpeed "10"
120 #define DEF_maxFalling "500"
121 #define DEF_maxColors "7"
123 #define DEF_spawn "50"
124 #define DEF_resolution "4"
126 #define DEF_camY "20"
127 #define DEF_camZ "25"
128 #define DEF_dropSpeed "4"
130 static argtype vars[] = {
131 {&override, "override", "Override", DEF_override, t_Bool},
132 {&rotate, "rotate", "Rotate", DEF_rotate, t_Bool},
133 {&drawCarpet, "carpet", "Carpet", DEF_drawCarpet, t_Bool},
134 {&drawNipples, "nipples", "Nipples", DEF_drawNipples, t_Bool},
135 {&drawBlob, "blob", "Blob", DEF_drawBlob, t_Bool},
136 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_rotateSpeed, t_Float},
137 {&follow, "follow", "Follow", DEF_follow, t_Bool},
138 {&camX, "camX", "camX", DEF_camX, t_Float},
139 {&camY, "camY", "camY", DEF_camY, t_Float},
140 {&camZ, "camZ", "camZ", DEF_camZ, t_Float},
141 {&size, "size", "size", DEF_size, t_Int},
142 {&spawn, "spawn", "spawn", DEF_spawn, t_Int},
143 {&maxFalling, "maxFalling", "maxFalling", DEF_maxFalling, t_Int},
144 {&resolution, "resolution", "resolution", DEF_resolution, t_Int},
145 {&maxColors, "maxColors", "maxColors", DEF_maxColors, t_Int},
146 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_dropSpeed, t_Float},
149 static topBlockSTATE *tbs = NULL;
151 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
153 /* Window management, etc */
155 reshape_topBlock (ModeInfo *mi, int width, int height) {
156 GLfloat h = (GLfloat) height / (GLfloat) width;
157 glViewport (0, 0, (GLint) width, (GLint) height);
158 glMatrixMode(GL_PROJECTION);
160 gluPerspective (60.0, 1/h, 1.0, 1000.0);
161 glMatrixMode(GL_MODELVIEW);
163 glClear(GL_COLOR_BUFFER_BIT);
166 /* clean up on exit, not required ... */
168 release_topBlock(ModeInfo *mi) {
169 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
170 NODE *llCurrent, *llOld;
171 llCurrent = tb->blockNodeRoot;
172 while (llCurrent != NULL) {
174 llCurrent = llCurrent->next;
181 init_topBlock (ModeInfo *mi)
184 int wire = MI_IS_WIREFRAME(mi);
187 tbs = (topBlockSTATE *)
188 calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
190 fprintf(stderr, "%s: out of memory\n", progname);
195 tb = &tbs[MI_SCREEN(mi)];
197 tb->glx_context = init_GL(mi);
199 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
201 if (wire) { drawNipples=False; }
202 tb->numFallingBlocks=0;
204 if (size>10) { size = 10; }
205 if (size<1) { size = 2; }
206 tb->carpetWidth = 8 * size;
207 tb->carpetLength = tb->carpetWidth;
211 if (spawn<4) { spawn=4; }
212 if (spawn>1000) { spawn=1000; }
214 if (rotateSpeed<1) {rotateSpeed=1; }
215 if (rotateSpeed>1000) {rotateSpeed=1000;}
218 if (resolution<4) {resolution=4;}
219 if (resolution>20) {resolution=20;}
222 if (maxColors<1) {maxColors=1;}
223 if (maxColors>8) {maxColors=8;}
225 if (dropSpeed<1) {dropSpeed=1;}
226 if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
228 dropSpeed = 80/dropSpeed;
229 dropSpeed = (blockHeight/dropSpeed);
231 tb->glx_context = init_GL(mi);
233 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
235 glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
237 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
241 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
242 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
243 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
244 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
246 glEnable(GL_LIGHTING);
248 glLightfv(GL_LIGHT0, GL_POSITION, pos);
249 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
250 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
251 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
253 glDepthFunc(GL_LEQUAL);
254 glEnable(GL_DEPTH_TEST);
255 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
261 buildBlock(mi); /* build the display list holding the simple block */
263 buildCarpet(mi); /* build the base */
265 tb->highestFalling=0;
266 tb->eyeLine=tb->highest;
275 tb->rotation=random() % 360;
280 /* override camera settings */
291 tb->trackball = gltrackball_init ();
294 /* provides the per frame entertainment */
296 draw_topBlock (ModeInfo *mi) {
297 Display *dpy = MI_DISPLAY(mi);
298 Window window = MI_WINDOW(mi);
301 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
302 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
303 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
304 int wire = MI_IS_WIREFRAME(mi);
307 if (!tb->glx_context)
309 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
311 generateNewBlock(mi);
313 if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; }
314 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
316 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
317 glLoadIdentity(); /* resets directions, do it every time ! */
319 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100); } /* creates a smooth camera transition */
320 gluLookAt(camX, camY+tb->eyeLine, camZ, tb->eyeX, tb->eyeY+tb->eyeLine, tb->eyeZ, 0.0, 1.0, 0.0); /* setup viewer, xyz cam, xyz looking at and where is up normaly 0,1,0 */
321 glRotatef(90, 1.0, 0.0, 0.0); /* x axis */
323 glRotatef(90, 0.0, 0.0, 1.0); /* z axis */
326 glRotatef(-90, 1.0, 0.0, 0.0);
327 gltrackball_rotate (tb->trackball);
328 glRotatef(90, 1.0, 0.0, 0.0);
330 /* rotate the world */
331 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
333 llCurrent = tb->blockNodeRoot;
336 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
337 glCallList(tb->carpet);
338 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
339 glTranslatef(0.0,0.0,-0.55);
341 tb->highestFalling=0;
342 while (llCurrent != NULL) { /* for each block */
343 glPushMatrix(); /* save state */
345 switch (llCurrent->color) {
395 if (wire) { glColor3fv(color); }
396 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
398 if (llCurrent->falling==1) {
403 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
404 /* all blocks fall at the same rate to avoid mid air collisions */
405 llCurrent->height-=dropSpeed;
406 if (llCurrent->height<=0) {
407 llCurrent->falling=0;
408 if (tb->highest==0) {
409 tb->highest+=blockHeight;
412 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
413 /* check for collision */
414 llNode = tb->blockNodeRoot;
415 spcC1x = llCurrent->x;
416 spcC1y = llCurrent->y;
417 switch(llCurrent->rotation) {
418 case getOrientation(0):
422 case getOrientation(1):
426 case getOrientation(2):
430 case getOrientation(3):
435 while (llNode != NULL) {
436 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
439 switch(llNode->rotation) {
440 case getOrientation(0):
444 case getOrientation(1):
448 case getOrientation(2):
452 case getOrientation(3):
458 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
459 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
460 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
461 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
463 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
465 llCurrent->falling=0;
466 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
467 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
468 tb->highest+=blockHeight;
477 /* set location in space */
478 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
480 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
481 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
482 tb->blockNodeFollow = llCurrent;
485 llCurrent = llCurrent->next;
487 glCallList(tb->block);
488 glPopMatrix(); /* restore state */
490 if (mi->fps_p) do_fps (mi);
493 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
494 glXSwapBuffers(dpy, window);
499 /* camera is in follow mode, work out where we should be looking */
500 static void followBlock(ModeInfo *mi) {
501 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
502 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
504 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
506 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
507 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
509 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
510 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
513 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
514 (see http://www.jumpstation.co.uk/rotation/)
517 if (tb->followRadius==0) {
518 xLen = tb->blockNodeFollow->x-cx;
519 yLen = tb->blockNodeFollow->y-cy;
520 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
521 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
522 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
524 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
525 xTarget = cos(rangle) * tb->followRadius + cx;
526 yTarget = sin(rangle) * tb->followRadius + cy;
527 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
529 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
530 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
532 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
533 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
534 if (!tb->blockNodeFollow->falling) {
536 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
540 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
543 /* each quater of the circle has to be adjusted for */
544 static double quadrantCorrection(double angle,int cx,int cy,int x,int y) {
545 if ((x>=cx) && (y>=cy)) {
546 angle += (90-(angle-90) * 2);
547 } else if ((x>=cx) && (y<=cy)) {
549 } else if ((x<=cx) && (y<=cy)) {
551 } else if ((x<=cx) && (y>=cy)) {
552 angle += (90-(angle-90) * 2);
557 /* if random chance then create a new falling block */
558 static void generateNewBlock(ModeInfo *mi) {
559 NODE *llCurrent, *llTail;
560 GLfloat startOffx, startOffy;
561 int endOffx, endOffy;
562 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
563 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
568 tb->numFallingBlocks++;
569 llTail = tb->blockNodeRoot;
570 if (llTail == NULL) {
571 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory.\n", progname); }
573 tb->blockNodeRoot = llCurrent;
575 if (tb->numFallingBlocks>=maxFalling) {
577 llCurrent=llTail->next;
578 tb->blockNodeRoot=llCurrent->next;
580 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory..\n", progname); }
582 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
584 llCurrent->falling=1;
585 llCurrent->rotation=getOrientation(random() % 4);
586 if (llCurrent->rotation==getOrientation(0)) {
591 } else if (llCurrent->rotation==getOrientation(1)) {
596 } else if (llCurrent->rotation==getOrientation(2)) {
601 } else if (llCurrent->rotation==getOrientation(3)) {
608 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
609 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
610 llCurrent->color=(random() % maxColors);
611 llCurrent->height=getHeight(tb->plusheight+tb->highest);
612 if (tb->numFallingBlocks>=maxFalling) {
613 tb->numFallingBlocks--;
614 tb->numFallingBlocks--;
616 llTail->next = llCurrent;
623 /* called at init this creates the 'carpet' display list item */
624 static void buildCarpet(ModeInfo *mi) {
627 int wire = MI_IS_WIREFRAME(mi);
628 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
629 GLUquadricObj *quadratic;
634 tb->carpet=glGenLists(1); /* only one */
635 glNewList(tb->carpet,GL_COMPILE);
636 glPushMatrix(); /* save state */
639 if (wire) { glColor3fv(color); }
640 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
641 /* draw carpet plane */
642 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
644 glNormal3f( 0, 0, -1 );
645 glVertex3f(0.0,0.0,0.0);
646 glVertex3f(x,0.0,0.0);
648 glVertex3f(0.0,y,0.0);
649 if (wire) { glEnd(); }
651 /* add edge pieces */
653 glNormal3f( 0, -1, 0 );
654 glVertex3f(0.0,0.0,0.0);
655 glVertex3f(x,0.0,0.0);
656 glVertex3f(x,0,singleThick);
657 glVertex3f(0.0,0,singleThick);
659 glNormal3f( -1, 0, 0 );
660 glVertex3f(0.0,0.0,0.0);
662 glVertex3f(0,y,singleThick);
663 glVertex3f(0.0,0,singleThick);
665 glNormal3f( 1, 0, 0 );
666 glVertex3f(x,0.0,0.0);
668 glVertex3f(x,y,singleThick);
669 glVertex3f(x,0,singleThick);
671 glNormal3f( 0, 1, 0 );
674 glVertex3f(x,y,singleThick);
675 glVertex3f(0,y,singleThick);
680 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
681 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
682 gluQuadricTexture(quadratic, GL_TRUE); /* Create Texture Coords */
683 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
685 glPushMatrix(); /* save state */
687 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
688 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
689 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
690 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
691 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
693 glPopMatrix(); /* save state */
694 glTranslatef(1.0f,0.0f,0.0f); /* reset */
697 glPopMatrix(); /* restore state */
701 /* using the verticies arrays builds the plane, now with normals */
702 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
704 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
705 GLfloat topBlockVertices[8][3] = { {-0.49,-2.97,-0.99}, {0.99,-2.97,-0.99}, {0.99,0.99,-0.99} , {-0.49,0.99,-0.99}, {-0.49,-2.97,0.99} , {0.99,-2.97,0.99}, {0.99,0.99,0.99} , {-0.49,0.99,0.99} };
706 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
707 glNormal3fv(topBlockNormals[i] );
708 glVertex3fv(topBlockVertices[a]);
709 glVertex3fv(topBlockVertices[b]);
710 glVertex3fv(topBlockVertices[c]);
711 glVertex3fv(topBlockVertices[d]);
715 /* called at init this creates the 'block' display list item */
716 /* the spheres came about originaly as quick way to test the directional lighting/normals */
717 static void buildBlock(ModeInfo *mi) {
719 int wire = MI_IS_WIREFRAME(mi);
720 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
721 GLUquadricObj *quadratic;
722 tb->block=glGenLists(1); /* only one */
723 glNewList(tb->block,GL_COMPILE);
724 glPushMatrix(); /* save state */
725 glRotatef(90, 0.0f, 1.0f, 0.0f);
727 polygonPlane(wire, 0,3,2,1,0);
728 polygonPlane(wire, 2,3,7,6,1);
729 polygonPlane(wire, 1,2,6,5,2);
730 polygonPlane(wire, 4,5,6,7,3);
731 polygonPlane(wire, 0,1,5,4,4);
734 /* draw 8 cylinders each with a disk cap */
735 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
736 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
737 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
738 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
741 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
742 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
743 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
744 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
746 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
748 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
751 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
754 /* 3 cylinders on the underside */
755 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
757 gluCylinder(quadratic, uddSize, uddSize, 1.5f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
758 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
761 glPopMatrix(); /* restore state */
766 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
767 spheres are created with unit_sphere from spheres.h to allow wire frame
769 static void buildBlobBlock(ModeInfo *mi) {
770 int wire = MI_IS_WIREFRAME(mi);
771 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
772 tb->block=glGenLists(1); /* only one */
773 glNewList(tb->block,GL_COMPILE);
775 glScalef(1.4,1.4,1.4);
776 unit_sphere (resolution/2,resolution, wire);
778 glTranslatef(0.0f,-2.0f,0.0f);
779 glScalef(1.4,1.4,1.4);
780 unit_sphere (resolution/2,resolution, wire);
785 /* handle input events or not if daemon running the show */
787 topBlock_handle_event (ModeInfo *mi, XEvent *event) {
788 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
789 if (event->xany.type == KeyPress) {
792 XLookupString (&event->xkey, &c, 1, &keysym, 0);
796 } else if (c == 'z') {
799 } else if (c == 's') {
802 } else if (c == 'x') {
805 } else if (c == 'd') {
808 } else if (c == 'c') {
811 } else if (c == 'f') {
814 } else if (c == 'v') {
817 } else if (c == 'g') {
820 } else if (c == 'b') {
823 } else if (c == 'h') {
826 } else if (c == 'n') {
829 } else if (c == 'r') {
834 if (event->xany.type == ButtonPress &&
835 event->xbutton.button == Button1)
837 tb->button_down_p = True;
838 gltrackball_start (tb->trackball,
839 event->xbutton.x, event->xbutton.y,
840 MI_WIDTH (mi), MI_HEIGHT (mi));
843 else if (event->xany.type == ButtonRelease &&
844 event->xbutton.button == Button1)
846 tb->button_down_p = False;
849 else if (event->xany.type == ButtonPress &&
850 (event->xbutton.button == Button4 ||
851 event->xbutton.button == Button5 ||
852 event->xbutton.button == Button6 ||
853 event->xbutton.button == Button7))
855 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
856 !!event->xbutton.state);
859 else if (event->xany.type == MotionNotify &&
862 gltrackball_track (tb->trackball,
863 event->xmotion.x, event->xmotion.y,
864 MI_WIDTH (mi), MI_HEIGHT (mi));
870 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
871 XSCREENSAVER_MODULE_2 ("topBlock", topblock, topBlock)