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;
70 int carpet_polys, block_polys;
72 NODE *blockNodeFollow;
94 static XrmOptionDescRec opts[] = {
95 { "-size", ".size", XrmoptionSepArg, 0 },
96 { "-spawn", ".spawn", XrmoptionSepArg, 0 },
97 { "-camX", ".camX", XrmoptionSepArg, 0 },
98 { "-camY", ".camY", XrmoptionSepArg, 0 },
99 { "-camZ", ".camZ", XrmoptionSepArg, 0 },
100 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
101 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
102 { "+carpet", ".carpet", XrmoptionNoArg, "False" },
103 { "+nipples", ".nipples", XrmoptionNoArg, "False" },
104 { "-blob", ".blob", XrmoptionNoArg, "True" },
105 { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
106 { "-follow", ".follow", XrmoptionNoArg, "True" },
107 { "-maxFalling", ".maxFalling", XrmoptionSepArg, 0 },
108 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
109 { "-maxColors", ".maxColors", XrmoptionSepArg, 0 },
110 { "-dropSpeed", ".dropSpeed", XrmoptionSepArg, 0 },
111 { "-override", ".override", XrmoptionNoArg, "True" },
114 #define DEF_OVERRIDE "False"
115 #define DEF_ROTATE "True"
116 #define DEF_FOLLOW "False"
117 #define DEF_CARPET "True"
118 #define DEF_BLOB "False"
119 #define DEF_NIPPLES "True"
120 #define DEF_ROTATE_SPEED "10"
121 #define DEF_MAX_FALLING "500"
122 #define DEF_MAX_COLORS "7"
124 #define DEF_SPAWN "50"
125 #define DEF_RESOLUTION "4"
126 #define DEF_CAM_X "1"
127 #define DEF_CAM_Y "20"
128 #define DEF_CAM_Z "25"
129 #define DEF_DROP_SPEED "4"
131 static argtype vars[] = {
132 {&override, "override", "Override", DEF_OVERRIDE, t_Bool},
133 {&rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
134 {&drawCarpet, "carpet", "Carpet", DEF_CARPET, t_Bool},
135 {&drawNipples, "nipples", "Nipples", DEF_NIPPLES, t_Bool},
136 {&drawBlob, "blob", "Blob", DEF_BLOB, t_Bool},
137 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_ROTATE_SPEED, t_Float},
138 {&follow, "follow", "Follow", DEF_FOLLOW, t_Bool},
139 {&camX, "camX", "camX", DEF_CAM_X, t_Float},
140 {&camY, "camY", "camY", DEF_CAM_Y, t_Float},
141 {&camZ, "camZ", "camZ", DEF_CAM_Z, t_Float},
142 {&size, "size", "size", DEF_SIZE, t_Int},
143 {&spawn, "spawn", "spawn", DEF_SPAWN, t_Int},
144 {&maxFalling, "maxFalling", "maxFalling", DEF_MAX_FALLING, t_Int},
145 {&resolution, "resolution", "resolution", DEF_RESOLUTION, t_Int},
146 {&maxColors, "maxColors", "maxColors", DEF_MAX_COLORS, t_Int},
147 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_DROP_SPEED, t_Float},
150 static topBlockSTATE *tbs = NULL;
152 ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
154 /* Window management, etc */
156 reshape_topBlock (ModeInfo *mi, int width, int height)
158 GLfloat h = (GLfloat) height / (GLfloat) width;
159 glViewport (0, 0, (GLint) width, (GLint) height);
160 glMatrixMode(GL_PROJECTION);
162 gluPerspective (60.0, 1/h, 1.0, 1000.0);
163 glMatrixMode(GL_MODELVIEW);
165 glClear(GL_COLOR_BUFFER_BIT);
168 /* clean up on exit, not required ... */
170 release_topBlock(ModeInfo *mi)
172 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
173 NODE *llCurrent, *llOld;
174 llCurrent = tb->blockNodeRoot;
175 while (llCurrent != NULL) {
177 llCurrent = llCurrent->next;
184 init_topBlock (ModeInfo *mi)
187 int wire = MI_IS_WIREFRAME(mi);
190 tbs = (topBlockSTATE *)
191 calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
193 fprintf(stderr, "%s: out of memory\n", progname);
198 tb = &tbs[MI_SCREEN(mi)];
200 tb->glx_context = init_GL(mi);
202 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
204 if (wire) { drawNipples=False; }
205 tb->numFallingBlocks=0;
207 if (size>10) { size = 10; }
208 if (size<1) { size = 2; }
209 tb->carpetWidth = 8 * size;
210 tb->carpetLength = tb->carpetWidth;
214 if (spawn<4) { spawn=4; }
215 if (spawn>1000) { spawn=1000; }
217 if (rotateSpeed<1) {rotateSpeed=1; }
218 if (rotateSpeed>1000) {rotateSpeed=1000;}
221 if (resolution<4) {resolution=4;}
222 if (resolution>20) {resolution=20;}
225 if (maxColors<1) {maxColors=1;}
226 if (maxColors>8) {maxColors=8;}
228 if (dropSpeed<1) {dropSpeed=1;}
229 if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
231 dropSpeed = 80/dropSpeed;
232 dropSpeed = (blockHeight/dropSpeed);
234 tb->glx_context = init_GL(mi);
236 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
238 glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
240 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
244 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
245 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
246 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
247 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
249 glEnable(GL_LIGHTING);
251 glLightfv(GL_LIGHT0, GL_POSITION, pos);
252 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
253 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
254 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
256 glDepthFunc(GL_LEQUAL);
257 glEnable(GL_DEPTH_TEST);
258 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
264 buildBlock(mi); /* build the display list holding the simple block */
266 buildCarpet(mi); /* build the base */
268 tb->highestFalling=0;
269 tb->eyeLine=tb->highest;
278 tb->rotation=random() % 360;
283 /* override camera settings */
294 tb->trackball = gltrackball_init ();
297 /* provides the per frame entertainment */
299 draw_topBlock (ModeInfo *mi)
301 Display *dpy = MI_DISPLAY(mi);
302 Window window = MI_WINDOW(mi);
305 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
306 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
307 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
308 int wire = MI_IS_WIREFRAME(mi);
311 if (!tb->glx_context)
313 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
314 mi->polygon_count = 0;
316 generateNewBlock(mi);
318 if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; }
319 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
321 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
322 glLoadIdentity(); /* resets directions, do it every time ! */
324 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100); } /* creates a smooth camera transition */
325 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 */
326 glRotatef(90, 1.0, 0.0, 0.0); /* x axis */
328 glRotatef(90, 0.0, 0.0, 1.0); /* z axis */
331 glRotatef(-90, 1.0, 0.0, 0.0);
332 gltrackball_rotate (tb->trackball);
333 glRotatef(90, 1.0, 0.0, 0.0);
335 /* rotate the world */
336 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
338 llCurrent = tb->blockNodeRoot;
341 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
342 glCallList(tb->carpet);
343 mi->polygon_count += tb->carpet_polys;
344 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
345 glTranslatef(0.0,0.0,-0.55);
347 tb->highestFalling=0;
348 while (llCurrent != NULL) { /* for each block */
349 glPushMatrix(); /* save state */
351 switch (llCurrent->color) {
401 if (wire) { glColor3fv(color); }
402 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
404 if (llCurrent->falling==1) {
409 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
410 /* all blocks fall at the same rate to avoid mid air collisions */
411 llCurrent->height-=dropSpeed;
412 if (llCurrent->height<=0) {
413 llCurrent->falling=0;
414 if (tb->highest==0) {
415 tb->highest+=blockHeight;
418 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
419 /* check for collision */
420 llNode = tb->blockNodeRoot;
421 spcC1x = llCurrent->x;
422 spcC1y = llCurrent->y;
423 switch(llCurrent->rotation) {
424 case getOrientation(0):
428 case getOrientation(1):
432 case getOrientation(2):
436 case getOrientation(3):
441 while (llNode != NULL) {
442 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
445 switch(llNode->rotation) {
446 case getOrientation(0):
450 case getOrientation(1):
454 case getOrientation(2):
458 case getOrientation(3):
464 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
465 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
466 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
467 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
469 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
471 llCurrent->falling=0;
472 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
473 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
474 tb->highest+=blockHeight;
483 /* set location in space */
484 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
486 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
487 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
488 tb->blockNodeFollow = llCurrent;
491 llCurrent = llCurrent->next;
493 glCallList(tb->block);
494 mi->polygon_count += tb->block_polys;
495 glPopMatrix(); /* restore state */
497 if (mi->fps_p) do_fps (mi);
500 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
501 glXSwapBuffers(dpy, window);
506 /* camera is in follow mode, work out where we should be looking */
507 static void followBlock(ModeInfo *mi)
509 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
510 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
512 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
514 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
515 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
517 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
518 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
521 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
522 (see http://www.jumpstation.co.uk/rotation/)
525 if (tb->followRadius==0) {
526 xLen = tb->blockNodeFollow->x-cx;
527 yLen = tb->blockNodeFollow->y-cy;
528 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
529 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
530 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
532 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
533 xTarget = cos(rangle) * tb->followRadius + cx;
534 yTarget = sin(rangle) * tb->followRadius + cy;
535 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
537 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
538 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
540 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
541 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
542 if (!tb->blockNodeFollow->falling) {
544 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
548 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
551 /* each quater of the circle has to be adjusted for */
552 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
554 if ((x>=cx) && (y>=cy)) {
555 angle += (90-(angle-90) * 2);
556 } else if ((x>=cx) && (y<=cy)) {
558 } else if ((x<=cx) && (y<=cy)) {
560 } else if ((x<=cx) && (y>=cy)) {
561 angle += (90-(angle-90) * 2);
566 /* if random chance then create a new falling block */
567 static void generateNewBlock(ModeInfo *mi)
569 NODE *llCurrent, *llTail;
570 GLfloat startOffx, startOffy;
571 int endOffx, endOffy;
572 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
573 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
578 tb->numFallingBlocks++;
579 llTail = tb->blockNodeRoot;
580 if (llTail == NULL) {
581 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory.\n", progname); }
583 tb->blockNodeRoot = llCurrent;
585 if (tb->numFallingBlocks>=maxFalling) {
587 llCurrent=llTail->next;
588 tb->blockNodeRoot=llCurrent->next;
590 if ((llCurrent = ((NODE*) malloc(sizeof(NODE)))) == NULL) { fprintf(stderr, "%s: out of memory..\n", progname); }
592 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
594 llCurrent->falling=1;
595 llCurrent->rotation=getOrientation(random() % 4);
596 if (llCurrent->rotation==getOrientation(0)) {
601 } else if (llCurrent->rotation==getOrientation(1)) {
606 } else if (llCurrent->rotation==getOrientation(2)) {
611 } else if (llCurrent->rotation==getOrientation(3)) {
618 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
619 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
620 llCurrent->color=(random() % maxColors);
621 llCurrent->height=getHeight(tb->plusheight+tb->highest);
622 if (tb->numFallingBlocks>=maxFalling) {
623 tb->numFallingBlocks--;
624 tb->numFallingBlocks--;
626 llTail->next = llCurrent;
633 /* called at init this creates the 'carpet' display list item */
634 static void buildCarpet(ModeInfo *mi)
638 int wire = MI_IS_WIREFRAME(mi);
639 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
640 GLUquadricObj *quadratic;
645 tb->carpet=glGenLists(1); /* only one */
646 glNewList(tb->carpet,GL_COMPILE);
648 glPushMatrix(); /* save state */
651 if (wire) { glColor3fv(color); }
652 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
653 /* draw carpet plane */
654 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
656 glNormal3f( 0, 0, -1 );
657 glVertex3f(0.0,0.0,0.0);
658 glVertex3f(x,0.0,0.0);
660 glVertex3f(0.0,y,0.0);
662 if (wire) { glEnd(); }
664 /* add edge pieces */
666 glNormal3f( 0, -1, 0 );
667 glVertex3f(0.0,0.0,0.0);
668 glVertex3f(x,0.0,0.0);
669 glVertex3f(x,0,singleThick);
670 glVertex3f(0.0,0,singleThick);
673 glNormal3f( -1, 0, 0 );
674 glVertex3f(0.0,0.0,0.0);
676 glVertex3f(0,y,singleThick);
677 glVertex3f(0.0,0,singleThick);
680 glNormal3f( 1, 0, 0 );
681 glVertex3f(x,0.0,0.0);
683 glVertex3f(x,y,singleThick);
684 glVertex3f(x,0,singleThick);
687 glNormal3f( 0, 1, 0 );
690 glVertex3f(x,y,singleThick);
691 glVertex3f(0,y,singleThick);
697 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
698 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
699 gluQuadricTexture(quadratic, GL_TRUE); /* Create Texture Coords */
700 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
702 glPushMatrix(); /* save state */
704 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
705 tb->carpet_polys += resolution*resolution;
706 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
707 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
708 tb->carpet_polys += resolution*resolution;
709 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
710 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
712 glPopMatrix(); /* save state */
713 glTranslatef(1.0f,0.0f,0.0f); /* reset */
716 glPopMatrix(); /* restore state */
720 /* using the verticies arrays builds the plane, now with normals */
721 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
723 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
724 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} };
725 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
726 glNormal3fv(topBlockNormals[i] );
727 glVertex3fv(topBlockVertices[a]);
728 glVertex3fv(topBlockVertices[b]);
729 glVertex3fv(topBlockVertices[c]);
730 glVertex3fv(topBlockVertices[d]);
734 /* called at init this creates the 'block' display list item */
735 /* the spheres came about originaly as quick way to test the directional lighting/normals */
736 static void buildBlock(ModeInfo *mi)
739 int wire = MI_IS_WIREFRAME(mi);
740 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
741 GLUquadricObj *quadratic;
742 tb->block=glGenLists(1); /* only one */
743 glNewList(tb->block,GL_COMPILE);
745 glPushMatrix(); /* save state */
746 glRotatef(90, 0.0f, 1.0f, 0.0f);
748 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
749 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
750 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
751 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
752 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
755 /* draw 8 cylinders each with a disk cap */
756 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
757 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
758 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
759 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
762 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
763 tb->block_polys += resolution*resolution;
764 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
765 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
766 tb->block_polys += resolution*resolution;
767 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
769 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
771 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
774 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
777 /* 3 cylinders on the underside */
778 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
780 gluCylinder(quadratic, uddSize, uddSize, 1.5f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
781 tb->block_polys += resolution*resolution;
782 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
785 glPopMatrix(); /* restore state */
790 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
791 spheres are created with unit_sphere from spheres.h to allow wire frame
793 static void buildBlobBlock(ModeInfo *mi)
795 int wire = MI_IS_WIREFRAME(mi);
796 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
797 tb->block=glGenLists(1); /* only one */
798 glNewList(tb->block,GL_COMPILE);
800 glScalef(1.4,1.4,1.4);
801 unit_sphere (resolution/2,resolution, wire);
803 glTranslatef(0.0f,-2.0f,0.0f);
804 glScalef(1.4,1.4,1.4);
805 unit_sphere (resolution/2,resolution, wire);
810 /* handle input events or not if daemon running the show */
812 topBlock_handle_event (ModeInfo *mi, XEvent *event)
814 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
815 if (event->xany.type == KeyPress) {
818 XLookupString (&event->xkey, &c, 1, &keysym, 0);
822 } else if (c == 'z') {
825 } else if (c == 's') {
828 } else if (c == 'x') {
831 } else if (c == 'd') {
834 } else if (c == 'c') {
837 } else if (c == 'f') {
840 } else if (c == 'v') {
843 } else if (c == 'g') {
846 } else if (c == 'b') {
849 } else if (c == 'h') {
852 } else if (c == 'n') {
855 } else if (c == 'r') {
860 if (event->xany.type == ButtonPress &&
861 event->xbutton.button == Button1)
863 tb->button_down_p = True;
864 gltrackball_start (tb->trackball,
865 event->xbutton.x, event->xbutton.y,
866 MI_WIDTH (mi), MI_HEIGHT (mi));
869 else if (event->xany.type == ButtonRelease &&
870 event->xbutton.button == Button1)
872 tb->button_down_p = False;
875 else if (event->xany.type == ButtonPress &&
876 (event->xbutton.button == Button4 ||
877 event->xbutton.button == Button5 ||
878 event->xbutton.button == Button6 ||
879 event->xbutton.button == Button7))
881 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
882 !!event->xbutton.state);
885 else if (event->xany.type == MotionNotify &&
888 gltrackball_track (tb->trackball,
889 event->xmotion.x, event->xmotion.y,
890 MI_WIDTH (mi), MI_HEIGHT (mi));
896 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
897 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)