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 ();
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 llCurrent = tb->blockNodeRoot;
333 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
334 glCallList(tb->carpet);
335 mi->polygon_count += tb->carpet_polys;
336 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
337 glTranslatef(0.0,0.0,-0.55);
339 tb->highestFalling=0;
340 while (llCurrent != NULL) { /* for each block */
341 glPushMatrix(); /* save state */
343 switch (llCurrent->color) {
393 if (wire) { glColor3fv(color); }
394 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
396 if (llCurrent->falling==1) {
401 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
402 /* all blocks fall at the same rate to avoid mid air collisions */
403 llCurrent->height-=dropSpeed;
404 if (llCurrent->height<=0) {
405 llCurrent->falling=0;
406 if (tb->highest==0) {
407 tb->highest+=blockHeight;
410 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
411 /* check for collision */
412 llNode = tb->blockNodeRoot;
413 spcC1x = llCurrent->x;
414 spcC1y = llCurrent->y;
415 switch(llCurrent->rotation) {
416 case getOrientation(0):
420 case getOrientation(1):
424 case getOrientation(2):
428 case getOrientation(3):
433 while (llNode != NULL) {
434 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
437 switch(llNode->rotation) {
438 case getOrientation(0):
442 case getOrientation(1):
446 case getOrientation(2):
450 case getOrientation(3):
456 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
457 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
458 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
459 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
461 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
463 llCurrent->falling=0;
464 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
465 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
466 tb->highest+=blockHeight;
475 /* set location in space */
476 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
478 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
479 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
480 tb->blockNodeFollow = llCurrent;
483 llCurrent = llCurrent->next;
485 glCallList(tb->block);
486 mi->polygon_count += tb->block_polys;
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)
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)
546 if ((x>=cx) && (y>=cy)) {
547 angle += (90-(angle-90) * 2);
548 } else if ((x>=cx) && (y<=cy)) {
550 } else if ((x<=cx) && (y<=cy)) {
552 } else if ((x<=cx) && (y>=cy)) {
553 angle += (90-(angle-90) * 2);
558 /* if random chance then create a new falling block */
559 static void generateNewBlock(ModeInfo *mi)
561 NODE *llCurrent, *llTail;
562 GLfloat startOffx, startOffy;
563 int endOffx, endOffy;
564 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
565 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
570 tb->numFallingBlocks++;
571 llTail = tb->blockNodeRoot;
572 if (llTail == NULL) {
573 llCurrent = ((NODE*) malloc(sizeof(NODE)));
574 if (!llCurrent) abort();
576 tb->blockNodeRoot = llCurrent;
578 if (tb->numFallingBlocks>=maxFalling) {
580 llCurrent=llTail->next;
581 tb->blockNodeRoot=llCurrent->next;
583 llCurrent = ((NODE*) malloc(sizeof(NODE)));
584 if (!llCurrent) abort();
586 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
588 llCurrent->falling=1;
589 llCurrent->rotation=getOrientation(random() % 4);
590 if (llCurrent->rotation==getOrientation(0)) {
595 } else if (llCurrent->rotation==getOrientation(1)) {
600 } else if (llCurrent->rotation==getOrientation(2)) {
605 } else if (llCurrent->rotation==getOrientation(3)) {
612 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
613 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
614 llCurrent->color=(random() % maxColors);
615 llCurrent->height=getHeight(tb->plusheight+tb->highest);
616 if (tb->numFallingBlocks>=maxFalling) {
617 tb->numFallingBlocks--;
618 tb->numFallingBlocks--;
620 llTail->next = llCurrent;
627 /* called at init this creates the 'carpet' display list item */
628 static void buildCarpet(ModeInfo *mi)
632 int wire = MI_IS_WIREFRAME(mi);
633 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
638 tb->carpet=glGenLists(1); /* only one */
639 glNewList(tb->carpet,GL_COMPILE);
641 glPushMatrix(); /* save state */
644 if (wire) { glColor3fv(color); }
645 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
646 /* draw carpet plane */
647 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
649 glNormal3f( 0, 0, -1 );
650 glVertex3f(0.0,0.0,0.0);
651 glVertex3f(x,0.0,0.0);
653 glVertex3f(0.0,y,0.0);
656 /* add edge pieces */
658 glNormal3f( 0, -1, 0 );
659 glVertex3f(0.0,0.0,0.0);
660 glVertex3f(x,0.0,0.0);
661 glVertex3f(x,0,singleThick);
662 glVertex3f(0.0,0,singleThick);
665 glNormal3f( -1, 0, 0 );
666 glVertex3f(0.0,0.0,0.0);
668 glVertex3f(0,y,singleThick);
669 glVertex3f(0.0,0,singleThick);
672 glNormal3f( 1, 0, 0 );
673 glVertex3f(x,0.0,0.0);
675 glVertex3f(x,y,singleThick);
676 glVertex3f(x,0,singleThick);
679 glNormal3f( 0, 1, 0 );
682 glVertex3f(x,y,singleThick);
683 glVertex3f(0,y,singleThick);
689 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
691 glPushMatrix(); /* save state */
693 tb->carpet_polys += tube(0, 0, -0.1,
696 resolution, True, True,
698 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
699 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
700 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
702 glPopMatrix(); /* save state */
703 glTranslatef(1.0f,0.0f,0.0f); /* reset */
706 glPopMatrix(); /* restore state */
710 /* using the verticies arrays builds the plane, now with normals */
711 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
713 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
714 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} };
715 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
716 glNormal3fv(topBlockNormals[i] );
717 glVertex3fv(topBlockVertices[a]);
718 glVertex3fv(topBlockVertices[b]);
719 glVertex3fv(topBlockVertices[c]);
720 glVertex3fv(topBlockVertices[d]);
724 /* called at init this creates the 'block' display list item */
725 /* the spheres came about originaly as quick way to test the directional lighting/normals */
726 static void buildBlock(ModeInfo *mi)
729 int wire = MI_IS_WIREFRAME(mi);
730 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
731 tb->block=glGenLists(1); /* only one */
732 glNewList(tb->block,GL_COMPILE);
734 glPushMatrix(); /* save state */
735 glRotatef(90, 0.0f, 1.0f, 0.0f);
737 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
738 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
739 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
740 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
741 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
744 /* draw 8 cylinders each with a disk cap */
745 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
746 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
749 tb->block_polys += tube(0, 0, 0,
752 resolution, True, True,
754 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
755 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
757 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
759 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
762 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
765 /* 3 cylinders on the underside */
766 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
769 tb->block_polys += tube(0, 0, 0.1,
772 resolution, True, True, wire);
773 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
776 glPopMatrix(); /* restore state */
781 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
782 spheres are created with unit_sphere from spheres.h to allow wire frame
784 static void buildBlobBlock(ModeInfo *mi)
786 int wire = MI_IS_WIREFRAME(mi);
787 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
788 tb->block=glGenLists(1); /* only one */
789 glNewList(tb->block,GL_COMPILE);
791 glScalef(1.4,1.4,1.4);
792 unit_sphere (resolution/2,resolution, wire);
794 glTranslatef(0.0f,-2.0f,0.0f);
795 glScalef(1.4,1.4,1.4);
796 unit_sphere (resolution/2,resolution, wire);
801 /* handle input events or not if daemon running the show */
803 topBlock_handle_event (ModeInfo *mi, XEvent *event)
805 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
806 if (event->xany.type == KeyPress) {
809 XLookupString (&event->xkey, &c, 1, &keysym, 0);
813 } else if (c == 'z') {
816 } else if (c == 's') {
819 } else if (c == 'x') {
822 } else if (c == 'd') {
825 } else if (c == 'c') {
828 } else if (c == 'f') {
831 } else if (c == 'v') {
834 } else if (c == 'g') {
837 } else if (c == 'b') {
840 } else if (c == 'h') {
843 } else if (c == 'n') {
846 } else if (c == 'r') {
851 if (event->xany.type == ButtonPress &&
852 event->xbutton.button == Button1)
854 tb->button_down_p = True;
855 gltrackball_start (tb->trackball,
856 event->xbutton.x, event->xbutton.y,
857 MI_WIDTH (mi), MI_HEIGHT (mi));
860 else if (event->xany.type == ButtonRelease &&
861 event->xbutton.button == Button1)
863 tb->button_down_p = False;
866 else if (event->xany.type == ButtonPress &&
867 (event->xbutton.button == Button4 ||
868 event->xbutton.button == Button5 ||
869 event->xbutton.button == Button6 ||
870 event->xbutton.button == Button7))
872 gltrackball_mousewheel (tb->trackball, event->xbutton.button, 10,
873 !!event->xbutton.state);
876 else if (event->xany.type == MotionNotify &&
879 gltrackball_track (tb->trackball,
880 event->xmotion.x, event->xmotion.y,
881 MI_WIDTH (mi), MI_HEIGHT (mi));
887 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
888 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)