1 /* topblock, Copyright (c) 2006-2009
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));
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 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
233 glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
235 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
239 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
240 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
241 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
242 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
244 glEnable(GL_LIGHTING);
246 glLightfv(GL_LIGHT0, GL_POSITION, pos);
247 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
248 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
249 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
251 glDepthFunc(GL_LEQUAL);
252 glEnable(GL_DEPTH_TEST);
253 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
259 buildBlock(mi); /* build the display list holding the simple block */
261 buildCarpet(mi); /* build the base */
263 tb->highestFalling=0;
264 tb->eyeLine=tb->highest;
273 tb->rotation=random() % 360;
278 /* override camera settings */
289 tb->trackball = gltrackball_init ();
292 /* provides the per frame entertainment */
294 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));
309 mi->polygon_count = 0;
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 mi->polygon_count += tb->carpet_polys;
339 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
340 glTranslatef(0.0,0.0,-0.55);
342 tb->highestFalling=0;
343 while (llCurrent != NULL) { /* for each block */
344 glPushMatrix(); /* save state */
346 switch (llCurrent->color) {
396 if (wire) { glColor3fv(color); }
397 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
399 if (llCurrent->falling==1) {
404 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
405 /* all blocks fall at the same rate to avoid mid air collisions */
406 llCurrent->height-=dropSpeed;
407 if (llCurrent->height<=0) {
408 llCurrent->falling=0;
409 if (tb->highest==0) {
410 tb->highest+=blockHeight;
413 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
414 /* check for collision */
415 llNode = tb->blockNodeRoot;
416 spcC1x = llCurrent->x;
417 spcC1y = llCurrent->y;
418 switch(llCurrent->rotation) {
419 case getOrientation(0):
423 case getOrientation(1):
427 case getOrientation(2):
431 case getOrientation(3):
436 while (llNode != NULL) {
437 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
440 switch(llNode->rotation) {
441 case getOrientation(0):
445 case getOrientation(1):
449 case getOrientation(2):
453 case getOrientation(3):
459 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
460 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
461 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
462 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
464 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
466 llCurrent->falling=0;
467 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
468 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
469 tb->highest+=blockHeight;
478 /* set location in space */
479 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
481 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
482 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
483 tb->blockNodeFollow = llCurrent;
486 llCurrent = llCurrent->next;
488 glCallList(tb->block);
489 mi->polygon_count += tb->block_polys;
490 glPopMatrix(); /* restore state */
492 if (mi->fps_p) do_fps (mi);
495 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
496 glXSwapBuffers(dpy, window);
501 /* camera is in follow mode, work out where we should be looking */
502 static void followBlock(ModeInfo *mi)
504 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
505 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
507 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
509 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
510 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
512 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
513 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
516 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
517 (see http://www.jumpstation.co.uk/rotation/)
520 if (tb->followRadius==0) {
521 xLen = tb->blockNodeFollow->x-cx;
522 yLen = tb->blockNodeFollow->y-cy;
523 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
524 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
525 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
527 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
528 xTarget = cos(rangle) * tb->followRadius + cx;
529 yTarget = sin(rangle) * tb->followRadius + cy;
530 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
532 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
533 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
535 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
536 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
537 if (!tb->blockNodeFollow->falling) {
539 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
543 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
546 /* each quater of the circle has to be adjusted for */
547 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
549 if ((x>=cx) && (y>=cy)) {
550 angle += (90-(angle-90) * 2);
551 } else if ((x>=cx) && (y<=cy)) {
553 } else if ((x<=cx) && (y<=cy)) {
555 } else if ((x<=cx) && (y>=cy)) {
556 angle += (90-(angle-90) * 2);
561 /* if random chance then create a new falling block */
562 static void generateNewBlock(ModeInfo *mi)
564 NODE *llCurrent, *llTail;
565 GLfloat startOffx, startOffy;
566 int endOffx, endOffy;
567 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
568 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
573 tb->numFallingBlocks++;
574 llTail = tb->blockNodeRoot;
575 if (llTail == NULL) {
576 llCurrent = ((NODE*) malloc(sizeof(NODE)));
577 if (!llCurrent) abort();
579 tb->blockNodeRoot = llCurrent;
581 if (tb->numFallingBlocks>=maxFalling) {
583 llCurrent=llTail->next;
584 tb->blockNodeRoot=llCurrent->next;
586 llCurrent = ((NODE*) malloc(sizeof(NODE)));
587 if (!llCurrent) abort();
589 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
591 llCurrent->falling=1;
592 llCurrent->rotation=getOrientation(random() % 4);
593 if (llCurrent->rotation==getOrientation(0)) {
598 } else if (llCurrent->rotation==getOrientation(1)) {
603 } else if (llCurrent->rotation==getOrientation(2)) {
608 } else if (llCurrent->rotation==getOrientation(3)) {
615 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
616 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
617 llCurrent->color=(random() % maxColors);
618 llCurrent->height=getHeight(tb->plusheight+tb->highest);
619 if (tb->numFallingBlocks>=maxFalling) {
620 tb->numFallingBlocks--;
621 tb->numFallingBlocks--;
623 llTail->next = llCurrent;
630 /* called at init this creates the 'carpet' display list item */
631 static void buildCarpet(ModeInfo *mi)
635 int wire = MI_IS_WIREFRAME(mi);
636 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
637 GLUquadricObj *quadratic;
642 tb->carpet=glGenLists(1); /* only one */
643 glNewList(tb->carpet,GL_COMPILE);
645 glPushMatrix(); /* save state */
648 if (wire) { glColor3fv(color); }
649 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
650 /* draw carpet plane */
651 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
653 glNormal3f( 0, 0, -1 );
654 glVertex3f(0.0,0.0,0.0);
655 glVertex3f(x,0.0,0.0);
657 glVertex3f(0.0,y,0.0);
659 if (wire) { glEnd(); }
661 /* add edge pieces */
663 glNormal3f( 0, -1, 0 );
664 glVertex3f(0.0,0.0,0.0);
665 glVertex3f(x,0.0,0.0);
666 glVertex3f(x,0,singleThick);
667 glVertex3f(0.0,0,singleThick);
670 glNormal3f( -1, 0, 0 );
671 glVertex3f(0.0,0.0,0.0);
673 glVertex3f(0,y,singleThick);
674 glVertex3f(0.0,0,singleThick);
677 glNormal3f( 1, 0, 0 );
678 glVertex3f(x,0.0,0.0);
680 glVertex3f(x,y,singleThick);
681 glVertex3f(x,0,singleThick);
684 glNormal3f( 0, 1, 0 );
687 glVertex3f(x,y,singleThick);
688 glVertex3f(0,y,singleThick);
694 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
695 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
696 gluQuadricTexture(quadratic, GL_TRUE); /* Create Texture Coords */
697 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
699 glPushMatrix(); /* save state */
701 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
702 tb->carpet_polys += resolution*resolution;
703 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
704 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
705 tb->carpet_polys += resolution*resolution;
706 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
707 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
709 glPopMatrix(); /* save state */
710 glTranslatef(1.0f,0.0f,0.0f); /* reset */
713 glPopMatrix(); /* restore state */
717 /* using the verticies arrays builds the plane, now with normals */
718 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
720 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
721 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} };
722 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
723 glNormal3fv(topBlockNormals[i] );
724 glVertex3fv(topBlockVertices[a]);
725 glVertex3fv(topBlockVertices[b]);
726 glVertex3fv(topBlockVertices[c]);
727 glVertex3fv(topBlockVertices[d]);
731 /* called at init this creates the 'block' display list item */
732 /* the spheres came about originaly as quick way to test the directional lighting/normals */
733 static void buildBlock(ModeInfo *mi)
736 int wire = MI_IS_WIREFRAME(mi);
737 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
738 GLUquadricObj *quadratic;
739 tb->block=glGenLists(1); /* only one */
740 glNewList(tb->block,GL_COMPILE);
742 glPushMatrix(); /* save state */
743 glRotatef(90, 0.0f, 1.0f, 0.0f);
745 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
746 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
747 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
748 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
749 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
752 /* draw 8 cylinders each with a disk cap */
753 quadratic=gluNewQuadric(); /* Create A Pointer To The Quadric Object */
754 gluQuadricNormals(quadratic, GLU_SMOOTH); /* Create Smooth Normals */
755 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
756 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
759 gluCylinder(quadratic, cylSize, cylSize, 0.25f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
760 tb->block_polys += resolution*resolution;
761 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
762 gluDisk(quadratic, 0.0f, cylSize, resolution, resolution ); /* inner size (cd hole), outer size (radius), subdivisions radial, subdivisions circular */
763 tb->block_polys += resolution*resolution;
764 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
766 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
768 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
771 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
774 /* 3 cylinders on the underside */
775 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
777 gluCylinder(quadratic, uddSize, uddSize, 1.5f, resolution, resolution); /* quad, radius(bottom, radius(top), height, subdivisions (around Z), subdevisions (along Z) */
778 tb->block_polys += resolution*resolution;
779 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
782 glPopMatrix(); /* restore state */
787 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
788 spheres are created with unit_sphere from spheres.h to allow wire frame
790 static void buildBlobBlock(ModeInfo *mi)
792 int wire = MI_IS_WIREFRAME(mi);
793 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
794 tb->block=glGenLists(1); /* only one */
795 glNewList(tb->block,GL_COMPILE);
797 glScalef(1.4,1.4,1.4);
798 unit_sphere (resolution/2,resolution, wire);
800 glTranslatef(0.0f,-2.0f,0.0f);
801 glScalef(1.4,1.4,1.4);
802 unit_sphere (resolution/2,resolution, wire);
807 /* handle input events or not if daemon running the show */
809 topBlock_handle_event (ModeInfo *mi, XEvent *event)
811 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
812 if (event->xany.type == KeyPress) {
815 XLookupString (&event->xkey, &c, 1, &keysym, 0);
819 } else if (c == 'z') {
822 } else if (c == 's') {
825 } else if (c == 'x') {
828 } else if (c == 'd') {
831 } else if (c == 'c') {
834 } else if (c == 'f') {
837 } else if (c == 'v') {
840 } else if (c == 'g') {
843 } else if (c == 'b') {
846 } else if (c == 'h') {
849 } else if (c == 'n') {
852 } else if (c == 'r') {
857 if (event->xany.type == ButtonPress &&
858 event->xbutton.button == Button1)
860 tb->button_down_p = True;
861 gltrackball_start (tb->trackball,
862 event->xbutton.x, event->xbutton.y,
863 MI_WIDTH (mi), MI_HEIGHT (mi));
866 else if (event->xany.type == ButtonRelease &&
867 event->xbutton.button == Button1)
869 tb->button_down_p = False;
872 else if (event->xany.type == ButtonPress &&
873 (event->xbutton.button == Button4 ||
874 event->xbutton.button == Button5 ||
875 event->xbutton.button == Button6 ||
876 event->xbutton.button == Button7))
878 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
879 !!event->xbutton.state);
882 else if (event->xany.type == MotionNotify &&
885 gltrackball_track (tb->trackball,
886 event->xmotion.x, event->xmotion.y,
887 MI_WIDTH (mi), MI_HEIGHT (mi));
893 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
894 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)