1 /* topblock, Copyright (c) 2006 rednuht <topblock.xscreensaver@jumpstation.co.uk>
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
14 topBlock - a simple openGL 3D hack of falling blocks
15 based on jwz's dangerball hack
17 The proporations of the blocks and their features is not even close to the commercial building block products offered by a variety companies.
19 information on this hack might be found at
20 http://www.jumpstation.co.uk/xscreensaver/topblock/
23 25/02/2006 v1.0 release
24 29/04/2006 v1.11 updated to better fit with xscreensaver v5
25 colors defaults to 7 (no black)
26 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.
31 # define refresh_topBlock 0
33 #define DEFAULTS "*delay: 10000 \n" \
35 "*showFPS: False \n" \
36 "*wireframe: False \n" \
39 #define countof(x) (sizeof((x))/sizeof((*x)))
41 #include "xlockmore.h"
44 #include "gltrackball.h"
47 #ifdef USE_GL /* whole file */
50 # include <OpenGL/glu.h>
57 GLXContext *glx_context;
58 trackball_state *trackball;
61 GLfloat highest,highestFalling;
62 GLfloat eyeLine,eyeX,eyeY,eyeZ;
63 int carpetWidth, carpetLength;
65 GLfloat followRadius,followAngle;
70 NODE *blockNodeFollow;
92 static XrmOptionDescRec opts[] = {
93 { "-size", ".size", XrmoptionSepArg, 0 },
94 { "-spawn", ".spawn", XrmoptionSepArg, 0 },
95 { "-camX", ".camX", XrmoptionSepArg, 0 },
96 { "-camY", ".camY", XrmoptionSepArg, 0 },
97 { "-camZ", ".camZ", XrmoptionSepArg, 0 },
98 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
99 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
100 { "+carpet", ".carpet", XrmoptionNoArg, "False" },
101 { "+nipples", ".nipples", XrmoptionNoArg, "False" },
102 { "-blob", ".blob", XrmoptionNoArg, "True" },
103 { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
104 { "-follow", ".follow", XrmoptionNoArg, "True" },
105 { "-maxFalling", ".maxFalling", XrmoptionSepArg, 0 },
106 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
107 { "-maxColors", ".maxColors", XrmoptionSepArg, 0 },
108 { "-dropSpeed", ".dropSpeed", XrmoptionSepArg, 0 },
109 { "-override", ".override", XrmoptionNoArg, "True" },
112 #define DEF_override "False"
113 #define DEF_rotate "True"
114 #define DEF_follow "False"
115 #define DEF_drawCarpet "True"
116 #define DEF_drawBlob "False"
117 #define DEF_drawNipples "True"
118 #define DEF_rotateSpeed "10"
119 #define DEF_maxFalling "500"
120 #define DEF_maxColors "7"
122 #define DEF_spawn "50"
123 #define DEF_resolution "4"
125 #define DEF_camY "20"
126 #define DEF_camZ "25"
127 #define DEF_dropSpeed "4"
129 static argtype vars[] = {
130 {&override, "override", "Override", DEF_override, t_Bool},
131 {&rotate, "rotate", "Rotate", DEF_rotate, t_Bool},
132 {&drawCarpet, "carpet", "Carpet", DEF_drawCarpet, t_Bool},
133 {&drawNipples, "nipples", "Nipples", DEF_drawNipples, t_Bool},
134 {&drawBlob, "blob", "Blob", DEF_drawBlob, t_Bool},
135 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_rotateSpeed, t_Float},
136 {&follow, "follow", "Follow", DEF_follow, t_Bool},
137 {&camX, "camX", "camX", DEF_camX, t_Float},
138 {&camY, "camY", "camY", DEF_camY, t_Float},
139 {&camZ, "camZ", "camZ", DEF_camZ, t_Float},
140 {&size, "size", "size", DEF_size, t_Int},
141 {&spawn, "spawn", "spawn", DEF_spawn, t_Int},
142 {&maxFalling, "maxFalling", "maxFalling", DEF_maxFalling, t_Int},
143 {&resolution, "resolution", "resolution", DEF_resolution, t_Int},
144 {&maxColors, "maxColors", "maxColors", DEF_maxColors, t_Int},
145 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_dropSpeed, t_Float},
148 static topBlockSTATE *tbs = NULL;
150 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
152 /* Window management, etc */
154 reshape_topBlock (ModeInfo *mi, int width, int height) {
155 GLfloat h = (GLfloat) height / (GLfloat) width;
156 glViewport (0, 0, (GLint) width, (GLint) height);
157 glMatrixMode(GL_PROJECTION);
159 gluPerspective (60.0, 1/h, 1.0, 1000.0);
160 glMatrixMode(GL_MODELVIEW);
162 glClear(GL_COLOR_BUFFER_BIT);
165 /* clean up on exit, not required ... */
167 release_topBlock(ModeInfo *mi) {
168 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
169 NODE *llCurrent, *llOld;
170 llCurrent = tb->blockNodeRoot;
171 while (llCurrent != NULL) {
173 llCurrent = llCurrent->next;
180 init_topBlock (ModeInfo *mi)
183 int wire = MI_IS_WIREFRAME(mi);
186 tbs = (topBlockSTATE *)
187 calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
189 fprintf(stderr, "%s: out of memory\n", progname);
194 tb = &tbs[MI_SCREEN(mi)];
196 tb->glx_context = init_GL(mi);
198 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
200 if (wire) { drawNipples=False; }
201 tb->numFallingBlocks=0;
203 if (size>10) { size = 10; }
204 if (size<1) { size = 2; }
205 tb->carpetWidth = 8 * size;
206 tb->carpetLength = tb->carpetWidth;
210 if (spawn<4) { spawn=4; }
211 if (spawn>1000) { spawn=1000; }
213 if (rotateSpeed<1) {rotateSpeed=1; }
214 if (rotateSpeed>1000) {rotateSpeed=1000;}
217 if (resolution<4) {resolution=4;}
218 if (resolution>20) {resolution=20;}
221 if (maxColors<1) {maxColors=1;}
222 if (maxColors>8) {maxColors=8;}
224 if (dropSpeed<1) {dropSpeed=1;}
225 if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
227 dropSpeed = 80/dropSpeed;
228 dropSpeed = (blockHeight/dropSpeed);
230 tb->glx_context = init_GL(mi);
232 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
234 glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
236 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
240 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
241 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
242 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
243 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
245 glEnable(GL_LIGHTING);
247 glLightfv(GL_LIGHT0, GL_POSITION, pos);
248 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
249 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
250 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
252 glDepthFunc(GL_LEQUAL);
253 glEnable(GL_DEPTH_TEST);
254 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
260 buildBlock(mi); /* build the display list holding the simple block */
262 buildCarpet(mi); /* build the base */
264 tb->highestFalling=0;
265 tb->eyeLine=tb->highest;
274 tb->rotation=random() % 360;
279 /* override camera settings */
290 tb->trackball = gltrackball_init ();
293 /* provides the per frame entertainment */
295 draw_topBlock (ModeInfo *mi) {
296 Display *dpy = MI_DISPLAY(mi);
297 Window window = MI_WINDOW(mi);
300 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
301 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
302 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
303 int wire = MI_IS_WIREFRAME(mi);
306 if (!tb->glx_context)
308 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
310 generateNewBlock(mi);
312 if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; }
313 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
315 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
316 glLoadIdentity(); /* resets directions, do it every time ! */
318 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100); } /* creates a smooth camera transition */
319 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 */
320 glRotatef(90, 1.0, 0.0, 0.0); /* x axis */
322 glRotatef(90, 0.0, 0.0, 1.0); /* z axis */
325 glRotatef(-90, 1.0, 0.0, 0.0);
326 gltrackball_rotate (tb->trackball);
327 glRotatef(90, 1.0, 0.0, 0.0);
329 /* rotate the world */
330 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
332 llCurrent = tb->blockNodeRoot;
335 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
336 glCallList(tb->carpet);
337 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
338 glTranslatef(0.0,0.0,-0.55);
340 tb->highestFalling=0;
341 while (llCurrent != NULL) { /* for each block */
342 glPushMatrix(); /* save state */
344 switch (llCurrent->color) {
394 if (wire) { glColor3fv(color); }
395 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
397 if (llCurrent->falling==1) {
402 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
403 /* all blocks fall at the same rate to avoid mid air collisions */
404 llCurrent->height-=dropSpeed;
405 if (llCurrent->height<=0) {
406 llCurrent->falling=0;
407 if (tb->highest==0) {
408 tb->highest+=blockHeight;
411 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
412 /* check for collision */
413 llNode = tb->blockNodeRoot;
414 spcC1x = llCurrent->x;
415 spcC1y = llCurrent->y;
416 switch(llCurrent->rotation) {
417 case getOrientation(0):
421 case getOrientation(1):
425 case getOrientation(2):
429 case getOrientation(3):
434 while (llNode != NULL) {
435 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
438 switch(llNode->rotation) {
439 case getOrientation(0):
443 case getOrientation(1):
447 case getOrientation(2):
451 case getOrientation(3):
457 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
458 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
459 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
460 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
462 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
464 llCurrent->falling=0;
465 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
466 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
467 tb->highest+=blockHeight;
476 /* set location in space */
477 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
479 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
480 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
481 tb->blockNodeFollow = llCurrent;
484 llCurrent = llCurrent->next;
486 glCallList(tb->block);
487 glPopMatrix(); /* restore state */
489 if (mi->fps_p) do_fps (mi);
492 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
493 glXSwapBuffers(dpy, window);
498 /* camera is in follow mode, work out where we should be looking */
499 static void followBlock(ModeInfo *mi) {
500 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
501 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
503 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
505 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
506 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
508 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
509 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
512 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
513 (see http://www.jumpstation.co.uk/rotation/)
516 if (tb->followRadius==0) {
517 xLen = tb->blockNodeFollow->x-cx;
518 yLen = tb->blockNodeFollow->y-cy;
519 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
520 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
521 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
523 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
524 xTarget = cos(rangle) * tb->followRadius + cx;
525 yTarget = sin(rangle) * tb->followRadius + cy;
526 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
528 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
529 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
531 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
532 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
533 if (!tb->blockNodeFollow->falling) {
535 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
539 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
542 /* each quater of the circle has to be adjusted for */
543 static double quadrantCorrection(double angle,int cx,int cy,int x,int y) {
544 if ((x>=cx) && (y>=cy)) {
545 angle += (90-(angle-90) * 2);
546 } else if ((x>=cx) && (y<=cy)) {
548 } else if ((x<=cx) && (y<=cy)) {
550 } else if ((x<=cx) && (y>=cy)) {
551 angle += (90-(angle-90) * 2);
556 /* if random chance then create a new falling block */
557 static void generateNewBlock(ModeInfo *mi) {
558 NODE *llCurrent, *llTail;
559 GLfloat startOffx, startOffy;
560 int endOffx, endOffy;
561 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
562 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
567 tb->numFallingBlocks++;
568 llTail = tb->blockNodeRoot;
569 if (llTail == NULL) {
570 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory.\n", progname); }
572 tb->blockNodeRoot = llCurrent;
574 if (tb->numFallingBlocks>=maxFalling) {
576 llCurrent=llTail->next;
577 tb->blockNodeRoot=llCurrent->next;
579 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory..\n", progname); }
581 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
583 llCurrent->falling=1;
584 llCurrent->rotation=getOrientation(random() % 4);
585 if (llCurrent->rotation==getOrientation(0)) {
590 } else if (llCurrent->rotation==getOrientation(1)) {
595 } else if (llCurrent->rotation==getOrientation(2)) {
600 } else if (llCurrent->rotation==getOrientation(3)) {
607 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
608 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
609 llCurrent->color=(random() % maxColors);
610 llCurrent->height=getHeight(tb->plusheight+tb->highest);
611 if (tb->numFallingBlocks>=maxFalling) {
612 tb->numFallingBlocks--;
613 tb->numFallingBlocks--;
615 llTail->next = llCurrent;
622 /* called at init this creates the 'carpet' display list item */
623 static void buildCarpet(ModeInfo *mi) {
626 int wire = MI_IS_WIREFRAME(mi);
627 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
628 GLUquadricObj *quadratic;
633 tb->carpet=glGenLists(1); /* only one */
634 glNewList(tb->carpet,GL_COMPILE);
635 glPushMatrix(); /* save state */
638 if (wire) { glColor3fv(color); }
639 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
640 /* draw carpet plane */
641 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
643 glNormal3f( 0, 0, -1 );
644 glVertex3f(0.0,0.0,0.0);
645 glVertex3f(x,0.0,0.0);
647 glVertex3f(0.0,y,0.0);
648 if (wire) { glEnd(); }
650 /* add edge pieces */
652 glNormal3f( 0, -1, 0 );
653 glVertex3f(0.0,0.0,0.0);
654 glVertex3f(x,0.0,0.0);
655 glVertex3f(x,0,singleThick);
656 glVertex3f(0.0,0,singleThick);
658 glNormal3f( -1, 0, 0 );
659 glVertex3f(0.0,0.0,0.0);
661 glVertex3f(0,y,singleThick);
662 glVertex3f(0.0,0,singleThick);
664 glNormal3f( 1, 0, 0 );
665 glVertex3f(x,0.0,0.0);
667 glVertex3f(x,y,singleThick);
668 glVertex3f(x,0,singleThick);
670 glNormal3f( 0, 1, 0 );
673 glVertex3f(x,y,singleThick);
674 glVertex3f(0,y,singleThick);
679 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
680 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
681 gluQuadricTexture(quadratic, GL_TRUE); /* Create Texture Coords */
682 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
684 glPushMatrix(); /* save state */
686 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
687 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
688 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
689 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
690 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
692 glPopMatrix(); /* save state */
693 glTranslatef(1.0f,0.0f,0.0f); /* reset */
696 glPopMatrix(); /* restore state */
700 /* using the verticies arrays builds the plane, now with normals */
701 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
703 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
704 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} };
705 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
706 glNormal3fv(topBlockNormals[i] );
707 glVertex3fv(topBlockVertices[a]);
708 glVertex3fv(topBlockVertices[b]);
709 glVertex3fv(topBlockVertices[c]);
710 glVertex3fv(topBlockVertices[d]);
714 /* called at init this creates the 'block' display list item */
715 /* the spheres came about originaly as quick way to test the directional lighting/normals */
716 static void buildBlock(ModeInfo *mi) {
718 int wire = MI_IS_WIREFRAME(mi);
719 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
720 GLUquadricObj *quadratic;
721 tb->block=glGenLists(1); /* only one */
722 glNewList(tb->block,GL_COMPILE);
723 glPushMatrix(); /* save state */
724 glRotatef(90, 0.0f, 1.0f, 0.0f);
726 polygonPlane(wire, 0,3,2,1,0);
727 polygonPlane(wire, 2,3,7,6,1);
728 polygonPlane(wire, 1,2,6,5,2);
729 polygonPlane(wire, 4,5,6,7,3);
730 polygonPlane(wire, 0,1,5,4,4);
733 /* draw 8 cylinders each with a disk cap */
734 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
735 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
736 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
737 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
740 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
741 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
742 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
743 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
745 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
747 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
750 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
753 /* 3 cylinders on the underside */
754 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
756 gluCylinder(quadratic, uddSize, uddSize, 1.5f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
757 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
760 glPopMatrix(); /* restore state */
765 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
766 spheres are created with unit_sphere from spheres.h to allow wire frame
768 static void buildBlobBlock(ModeInfo *mi) {
769 int wire = MI_IS_WIREFRAME(mi);
770 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
771 tb->block=glGenLists(1); /* only one */
772 glNewList(tb->block,GL_COMPILE);
774 glScalef(1.4,1.4,1.4);
775 unit_sphere (resolution/2,resolution, wire);
777 glTranslatef(0.0f,-2.0f,0.0f);
778 glScalef(1.4,1.4,1.4);
779 unit_sphere (resolution/2,resolution, wire);
784 /* handle input events or not if daemon running the show */
786 topBlock_handle_event (ModeInfo *mi, XEvent *event) {
787 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
788 if (event->xany.type == KeyPress) {
791 XLookupString (&event->xkey, &c, 1, &keysym, 0);
795 } else if (c == 'z') {
798 } else if (c == 's') {
801 } else if (c == 'x') {
804 } else if (c == 'd') {
807 } else if (c == 'c') {
810 } else if (c == 'f') {
813 } else if (c == 'v') {
816 } else if (c == 'g') {
819 } else if (c == 'b') {
822 } else if (c == 'h') {
825 } else if (c == 'n') {
828 } else if (c == 'r') {
833 if (event->xany.type == ButtonPress &&
834 event->xbutton.button == Button1)
836 tb->button_down_p = True;
837 gltrackball_start (tb->trackball,
838 event->xbutton.x, event->xbutton.y,
839 MI_WIDTH (mi), MI_HEIGHT (mi));
842 else if (event->xany.type == ButtonRelease &&
843 event->xbutton.button == Button1)
845 tb->button_down_p = False;
848 else if (event->xany.type == ButtonPress &&
849 (event->xbutton.button == Button4 ||
850 event->xbutton.button == Button5))
852 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
853 !!event->xbutton.state);
856 else if (event->xany.type == MotionNotify &&
859 gltrackball_track (tb->trackball,
860 event->xmotion.x, event->xmotion.y,
861 MI_WIDTH (mi), MI_HEIGHT (mi));
867 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
868 XSCREENSAVER_MODULE_2 ("topBlock", topblock, topBlock)