1 /* topblock, Copyright (c) 2006-2012 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"
45 #include "gltrackball.h"
48 #ifdef USE_GL /* whole file */
56 GLXContext *glx_context;
57 trackball_state *trackball;
60 GLfloat highest,highestFalling;
61 GLfloat eyeLine,eyeX,eyeY,eyeZ;
62 int carpetWidth, carpetLength;
64 GLfloat followRadius,followAngle;
68 int carpet_polys, block_polys;
70 NODE *blockNodeFollow;
78 static Bool drawCarpet;
80 static Bool drawNipples;
81 static GLfloat rotateSpeed;
85 static GLfloat dropSpeed;
86 static int maxFalling;
90 static int resolution;
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_CARPET "True"
116 #define DEF_BLOB "False"
117 #define DEF_NIPPLES "True"
118 #define DEF_ROTATE_SPEED "10"
119 #define DEF_MAX_FALLING "500"
120 #define DEF_MAX_COLORS "7"
122 #define DEF_SPAWN "50"
123 #define DEF_RESOLUTION "4"
124 #define DEF_CAM_X "1"
125 #define DEF_CAM_Y "20"
126 #define DEF_CAM_Z "25"
127 #define DEF_DROP_SPEED "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_CARPET, t_Bool},
133 {&drawNipples, "nipples", "Nipples", DEF_NIPPLES, t_Bool},
134 {&drawBlob, "blob", "Blob", DEF_BLOB, t_Bool},
135 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_ROTATE_SPEED, t_Float},
136 {&follow, "follow", "Follow", DEF_FOLLOW, t_Bool},
137 {&camX, "camX", "camX", DEF_CAM_X, t_Float},
138 {&camY, "camY", "camY", DEF_CAM_Y, t_Float},
139 {&camZ, "camZ", "camZ", DEF_CAM_Z, t_Float},
140 {&size, "size", "size", DEF_SIZE, t_Int},
141 {&spawn, "spawn", "spawn", DEF_SPAWN, t_Int},
142 {&maxFalling, "maxFalling", "maxFalling", DEF_MAX_FALLING, t_Int},
143 {&resolution, "resolution", "resolution", DEF_RESOLUTION, t_Int},
144 {&maxColors, "maxColors", "maxColors", DEF_MAX_COLORS, t_Int},
145 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_DROP_SPEED, t_Float},
148 static topBlockSTATE *tbs = NULL;
150 static ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
152 /* Window management, etc */
154 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)
170 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
171 NODE *llCurrent, *llOld;
172 llCurrent = tb->blockNodeRoot;
173 while (llCurrent != NULL) {
175 llCurrent = llCurrent->next;
182 init_topBlock (ModeInfo *mi)
185 int wire = MI_IS_WIREFRAME(mi);
188 tbs = (topBlockSTATE *)
189 calloc (MI_NUM_SCREENS(mi), sizeof (topBlockSTATE));
193 tb = &tbs[MI_SCREEN(mi)];
195 tb->glx_context = init_GL(mi);
197 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
199 /* if (wire) { drawNipples=False; }*/
200 tb->numFallingBlocks=0;
202 if (size>10) { size = 10; }
203 if (size<1) { size = 2; }
204 tb->carpetWidth = 8 * size;
205 tb->carpetLength = tb->carpetWidth;
209 if (spawn<4) { spawn=4; }
210 if (spawn>1000) { spawn=1000; }
212 if (rotateSpeed<1) {rotateSpeed=1; }
213 if (rotateSpeed>1000) {rotateSpeed=1000;}
216 if (resolution<4) {resolution=4;}
217 if (resolution>20) {resolution=20;}
220 if (maxColors<1) {maxColors=1;}
221 if (maxColors>8) {maxColors=8;}
223 if (dropSpeed<1) {dropSpeed=1;}
224 if (dropSpeed>9) {dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
226 dropSpeed = 80/dropSpeed;
227 dropSpeed = (blockHeight/dropSpeed);
229 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
232 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
233 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
234 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
235 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
237 glEnable(GL_LIGHTING);
239 glLightfv(GL_LIGHT0, GL_POSITION, pos);
240 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
241 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
242 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
244 glDepthFunc(GL_LEQUAL);
245 glEnable(GL_DEPTH_TEST);
246 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
252 buildBlock(mi); /* build the display list holding the simple block */
254 buildCarpet(mi); /* build the base */
256 tb->highestFalling=0;
257 tb->eyeLine=tb->highest;
266 tb->rotation=random() % 360;
271 /* override camera settings */
282 tb->trackball = gltrackball_init (False);
285 /* provides the per frame entertainment */
287 draw_topBlock (ModeInfo *mi)
289 Display *dpy = MI_DISPLAY(mi);
290 Window window = MI_WINDOW(mi);
293 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
294 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
295 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
296 int wire = MI_IS_WIREFRAME(mi);
299 if (!tb->glx_context)
301 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
302 mi->polygon_count = 0;
304 generateNewBlock(mi);
306 if (rotate && (!tb->button_down_p)) { tb->rotation += rotateSpeed; }
307 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
309 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
310 glLoadIdentity(); /* resets directions, do it every time ! */
311 glRotatef(current_device_rotation(), 0, 0, 1);
314 if (tb->highest>tb->eyeLine) { tb->eyeLine+=((tb->highest-tb->eyeLine)/100); } /* creates a smooth camera transition */
315 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 */
316 glRotatef(90, 1.0, 0.0, 0.0); /* x axis */
318 glRotatef(90, 0.0, 0.0, 1.0); /* z axis */
322 /* Rotate the scene around a point that's a little higher up. */
323 glTranslatef (0, 0, -5);
324 gltrackball_rotate (tb->trackball);
325 glTranslatef (0, 0, 5);
327 /* rotate the world */
328 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
330 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
332 GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
333 int o = (int) current_device_rotation();
334 if (o != 0 && o != 180 && o != -180)
335 glScalef (1/h, 1/h, 1/h);
339 llCurrent = tb->blockNodeRoot;
342 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
343 glCallList(tb->carpet);
344 mi->polygon_count += tb->carpet_polys;
345 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
346 glTranslatef(0.0,0.0,-0.55);
348 tb->highestFalling=0;
349 while (llCurrent != NULL) { /* for each block */
350 glPushMatrix(); /* save state */
352 switch (llCurrent->color) {
402 if (wire) { glColor3fv(color); }
403 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
405 if (llCurrent->falling==1) {
410 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
411 /* all blocks fall at the same rate to avoid mid air collisions */
412 llCurrent->height-=dropSpeed;
413 if (llCurrent->height<=0) {
414 llCurrent->falling=0;
415 if (tb->highest==0) {
416 tb->highest+=blockHeight;
419 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
420 /* check for collision */
421 llNode = tb->blockNodeRoot;
422 spcC1x = llCurrent->x;
423 spcC1y = llCurrent->y;
424 switch(llCurrent->rotation) {
425 case getOrientation(0):
429 case getOrientation(1):
433 case getOrientation(2):
437 case getOrientation(3):
442 while (llNode != NULL) {
443 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
446 switch(llNode->rotation) {
447 case getOrientation(0):
451 case getOrientation(1):
455 case getOrientation(2):
459 case getOrientation(3):
465 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
466 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
467 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
468 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
470 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
472 llCurrent->falling=0;
473 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
474 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
475 tb->highest+=blockHeight;
484 /* set location in space */
485 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
487 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
488 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
489 tb->blockNodeFollow = llCurrent;
492 llCurrent = llCurrent->next;
494 glCallList(tb->block);
495 mi->polygon_count += tb->block_polys;
496 glPopMatrix(); /* restore state */
498 if (mi->fps_p) do_fps (mi);
501 if (tb->highest>(5*maxFalling)) { drawCarpet=False; }
502 glXSwapBuffers(dpy, window);
507 /* camera is in follow mode, work out where we should be looking */
508 static void followBlock(ModeInfo *mi)
510 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
511 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
513 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
515 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
516 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
518 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
519 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
522 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
523 (see http://www.jumpstation.co.uk/rotation/)
526 if (tb->followRadius==0) {
527 xLen = tb->blockNodeFollow->x-cx;
528 yLen = tb->blockNodeFollow->y-cy;
529 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
530 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
531 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
533 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
534 xTarget = cos(rangle) * tb->followRadius + cx;
535 yTarget = sin(rangle) * tb->followRadius + cy;
536 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
538 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
539 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
541 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
542 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
543 if (!tb->blockNodeFollow->falling) {
545 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
549 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
552 /* each quater of the circle has to be adjusted for */
553 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
555 if ((x>=cx) && (y>=cy)) {
556 angle += (90-(angle-90) * 2);
557 } else if ((x>=cx) && (y<=cy)) {
559 } else if ((x<=cx) && (y<=cy)) {
561 } else if ((x<=cx) && (y>=cy)) {
562 angle += (90-(angle-90) * 2);
567 /* if random chance then create a new falling block */
568 static void generateNewBlock(ModeInfo *mi)
570 NODE *llCurrent, *llTail;
571 GLfloat startOffx, startOffy;
572 int endOffx, endOffy;
573 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
574 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
579 tb->numFallingBlocks++;
580 llTail = tb->blockNodeRoot;
581 if (llTail == NULL) {
582 llCurrent = ((NODE*) malloc(sizeof(NODE)));
583 if (!llCurrent) abort();
585 tb->blockNodeRoot = llCurrent;
587 if (tb->numFallingBlocks>=maxFalling) {
589 llCurrent=llTail->next;
590 tb->blockNodeRoot=llCurrent->next;
592 llCurrent = ((NODE*) malloc(sizeof(NODE)));
593 if (!llCurrent) abort();
595 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
597 llCurrent->falling=1;
598 llCurrent->rotation=getOrientation(random() % 4);
599 if (llCurrent->rotation==getOrientation(0)) {
604 } else if (llCurrent->rotation==getOrientation(1)) {
609 } else if (llCurrent->rotation==getOrientation(2)) {
614 } else if (llCurrent->rotation==getOrientation(3)) {
621 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
622 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
623 llCurrent->color=(random() % maxColors);
624 llCurrent->height=getHeight(tb->plusheight+tb->highest);
625 if (tb->numFallingBlocks>=maxFalling) {
626 tb->numFallingBlocks--;
627 tb->numFallingBlocks--;
629 llTail->next = llCurrent;
636 /* called at init this creates the 'carpet' display list item */
637 static void buildCarpet(ModeInfo *mi)
641 int wire = MI_IS_WIREFRAME(mi);
642 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
647 tb->carpet=glGenLists(1); /* only one */
648 glNewList(tb->carpet,GL_COMPILE);
650 glPushMatrix(); /* save state */
653 if (wire) { glColor3fv(color); }
654 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
655 /* draw carpet plane */
656 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
658 glNormal3f( 0, 0, -1 );
659 glVertex3f(0.0,0.0,0.0);
660 glVertex3f(x,0.0,0.0);
662 glVertex3f(0.0,y,0.0);
665 /* add edge pieces */
667 glNormal3f( 0, -1, 0 );
668 glVertex3f(0.0,0.0,0.0);
669 glVertex3f(x,0.0,0.0);
670 glVertex3f(x,0,singleThick);
671 glVertex3f(0.0,0,singleThick);
674 glNormal3f( -1, 0, 0 );
675 glVertex3f(0.0,0.0,0.0);
677 glVertex3f(0,y,singleThick);
678 glVertex3f(0.0,0,singleThick);
681 glNormal3f( 1, 0, 0 );
682 glVertex3f(x,0.0,0.0);
684 glVertex3f(x,y,singleThick);
685 glVertex3f(x,0,singleThick);
688 glNormal3f( 0, 1, 0 );
691 glVertex3f(x,y,singleThick);
692 glVertex3f(0,y,singleThick);
698 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
700 glPushMatrix(); /* save state */
702 tb->carpet_polys += tube(0, 0, -0.1,
705 resolution, True, True,
707 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
708 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
709 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
711 glPopMatrix(); /* save state */
712 glTranslatef(1.0f,0.0f,0.0f); /* reset */
715 glPopMatrix(); /* restore state */
719 /* using the verticies arrays builds the plane, now with normals */
720 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
722 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
723 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} };
724 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
725 glNormal3fv(topBlockNormals[i] );
726 glVertex3fv(topBlockVertices[a]);
727 glVertex3fv(topBlockVertices[b]);
728 glVertex3fv(topBlockVertices[c]);
729 glVertex3fv(topBlockVertices[d]);
733 /* called at init this creates the 'block' display list item */
734 /* the spheres came about originaly as quick way to test the directional lighting/normals */
735 static void buildBlock(ModeInfo *mi)
738 int wire = MI_IS_WIREFRAME(mi);
739 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
740 tb->block=glGenLists(1); /* only one */
741 glNewList(tb->block,GL_COMPILE);
743 glPushMatrix(); /* save state */
744 glRotatef(90, 0.0f, 1.0f, 0.0f);
746 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
747 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
748 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
749 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
750 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
753 /* draw 8 cylinders each with a disk cap */
754 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
755 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
758 tb->block_polys += tube(0, 0, 0,
761 resolution, True, True,
763 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
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 */
778 tb->block_polys += tube(0, 0, 0.1,
781 resolution, True, True, wire);
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)];
816 if (gltrackball_event_handler (event, tb->trackball,
817 MI_WIDTH (mi), MI_HEIGHT (mi),
820 else if (event->xany.type == KeyPress) {
823 XLookupString (&event->xkey, &c, 1, &keysym, 0);
827 } else if (c == 'z') {
830 } else if (c == 's') {
833 } else if (c == 'x') {
836 } else if (c == 'd') {
839 } else if (c == 'c') {
842 } else if (c == 'f') {
845 } else if (c == 'v') {
848 } else if (c == 'g') {
851 } else if (c == 'b') {
854 } else if (c == 'h') {
857 } else if (c == 'n') {
860 } else if (c == 'r') {
869 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
870 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)