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
32 # define release_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"
46 #include "gltrackball.h"
49 #ifdef USE_GL /* whole file */
57 GLXContext *glx_context;
58 trackball_state *trackball;
65 GLfloat highest,highestFalling;
66 GLfloat eyeLine,eyeX,eyeY,eyeZ;
67 int carpetWidth, carpetLength;
69 GLfloat followRadius,followAngle;
73 int carpet_polys, block_polys;
75 NODE *blockNodeFollow;
83 static Bool drawCarpet;
85 static Bool drawNipples;
86 static GLfloat rotateSpeed;
90 static GLfloat dropSpeed;
91 static int maxFalling;
95 static int resolution;
97 static XrmOptionDescRec opts[] = {
98 { "-size", ".size", XrmoptionSepArg, 0 },
99 { "-spawn", ".spawn", XrmoptionSepArg, 0 },
100 { "-camX", ".camX", XrmoptionSepArg, 0 },
101 { "-camY", ".camY", XrmoptionSepArg, 0 },
102 { "-camZ", ".camZ", XrmoptionSepArg, 0 },
103 { "+rotate", ".rotate", XrmoptionNoArg, "False" },
104 { "-rotate", ".rotate", XrmoptionNoArg, "True" },
105 { "+carpet", ".carpet", XrmoptionNoArg, "False" },
106 { "+nipples", ".nipples", XrmoptionNoArg, "False" },
107 { "-blob", ".blob", XrmoptionNoArg, "True" },
108 { "-rotateSpeed", ".rotateSpeed", XrmoptionSepArg, 0 },
109 { "-follow", ".follow", XrmoptionNoArg, "True" },
110 { "-maxFalling", ".maxFalling", XrmoptionSepArg, 0 },
111 { "-resolution", ".resolution", XrmoptionSepArg, 0 },
112 { "-maxColors", ".maxColors", XrmoptionSepArg, 0 },
113 { "-dropSpeed", ".dropSpeed", XrmoptionSepArg, 0 },
114 { "-override", ".override", XrmoptionNoArg, "True" },
117 #define DEF_OVERRIDE "False"
118 #define DEF_ROTATE "True"
119 #define DEF_FOLLOW "False"
120 #define DEF_CARPET "True"
121 #define DEF_BLOB "False"
122 #define DEF_NIPPLES "True"
123 #define DEF_ROTATE_SPEED "10"
124 #define DEF_MAX_FALLING "500"
125 #define DEF_MAX_COLORS "7"
127 #define DEF_SPAWN "50"
128 #define DEF_RESOLUTION "4"
129 #define DEF_CAM_X "1"
130 #define DEF_CAM_Y "20"
131 #define DEF_CAM_Z "25"
132 #define DEF_DROP_SPEED "4"
134 static argtype vars[] = {
135 {&override, "override", "Override", DEF_OVERRIDE, t_Bool},
136 {&rotate, "rotate", "Rotate", DEF_ROTATE, t_Bool},
137 {&drawCarpet, "carpet", "Carpet", DEF_CARPET, t_Bool},
138 {&drawNipples, "nipples", "Nipples", DEF_NIPPLES, t_Bool},
139 {&drawBlob, "blob", "Blob", DEF_BLOB, t_Bool},
140 {&rotateSpeed, "rotateSpeed", "RotateSpeed", DEF_ROTATE_SPEED, t_Float},
141 {&follow, "follow", "Follow", DEF_FOLLOW, t_Bool},
142 {&camX, "camX", "camX", DEF_CAM_X, t_Float},
143 {&camY, "camY", "camY", DEF_CAM_Y, t_Float},
144 {&camZ, "camZ", "camZ", DEF_CAM_Z, t_Float},
145 {&size, "size", "size", DEF_SIZE, t_Int},
146 {&spawn, "spawn", "spawn", DEF_SPAWN, t_Int},
147 {&maxFalling, "maxFalling", "maxFalling", DEF_MAX_FALLING, t_Int},
148 {&resolution, "resolution", "resolution", DEF_RESOLUTION, t_Int},
149 {&maxColors, "maxColors", "maxColors", DEF_MAX_COLORS, t_Int},
150 {&dropSpeed, "dropSpeed", "DropSpeed", DEF_DROP_SPEED, t_Float},
153 static topBlockSTATE *tbs = NULL;
155 static ModeSpecOpt topBlock_opts = {countof(opts), opts, countof(vars), vars, NULL};
157 /* Window management, etc */
159 reshape_topBlock (ModeInfo *mi, int width, int height)
161 GLfloat h = (GLfloat) height / (GLfloat) width;
162 glViewport (0, 0, (GLint) width, (GLint) height);
163 glMatrixMode(GL_PROJECTION);
165 gluPerspective (60.0, 1/h, 1.0, 1000.0);
166 glMatrixMode(GL_MODELVIEW);
168 glClear(GL_COLOR_BUFFER_BIT);
171 /* clean up on exit, not required ... */
173 free_topBlock(ModeInfo *mi)
175 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
176 NODE *llCurrent, *llOld;
177 llCurrent = tb->blockNodeRoot;
178 while (llCurrent != NULL) {
180 llCurrent = llCurrent->next;
187 init_topBlock (ModeInfo *mi)
190 int wire = MI_IS_WIREFRAME(mi);
192 MI_INIT (mi, tbs, free_topBlock);
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;
208 tb->maxFalling = maxFalling;
209 tb->maxFalling*=size;
211 if (spawn<4) { spawn=4; }
212 if (spawn>1000) { spawn=1000; }
214 tb->rotateSpeed = rotateSpeed;
215 if (tb->rotateSpeed<1) {tb->rotateSpeed=1; }
216 if (tb->rotateSpeed>1000) {tb->rotateSpeed=1000;}
217 tb->rotateSpeed /= 100;
219 tb->resolution = resolution;
220 if (tb->resolution<4) {tb->resolution=4;}
221 if (tb->resolution>20) {tb->resolution=20;}
224 if (maxColors<1) {maxColors=1;}
225 if (maxColors>8) {maxColors=8;}
227 tb->dropSpeed = dropSpeed;
228 if (tb->dropSpeed<1) {tb->dropSpeed=1;}
229 if (tb->dropSpeed>9) {tb->dropSpeed=9;} /* 10+ produces blocks that can pass through each other */
231 tb->dropSpeed = 80/tb->dropSpeed;
232 tb->dropSpeed = (blockHeight/tb->dropSpeed);
234 reshape_topBlock (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
237 GLfloat pos[4] = {10.0, 10.0, 1.0, 0.0};
238 GLfloat amb[4] = {0.1, 0.1, 0.1, 1.0};
239 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
240 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
242 glEnable(GL_LIGHTING);
244 glLightfv(GL_LIGHT0, GL_POSITION, pos);
245 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
246 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
247 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
249 glDepthFunc(GL_LEQUAL);
250 glEnable(GL_DEPTH_TEST);
251 glDisable(GL_CULL_FACE); /* all objects exhibit a reverse side */
257 buildBlock(mi); /* build the display list holding the simple block */
259 buildCarpet(mi); /* build the base */
261 tb->highestFalling=0;
262 tb->eyeLine=tb->highest;
271 tb->rotation=random() % 360;
276 /* override camera settings */
287 tb->trackball = gltrackball_init (False);
290 /* provides the per frame entertainment */
292 draw_topBlock (ModeInfo *mi)
294 Display *dpy = MI_DISPLAY(mi);
295 Window window = MI_WINDOW(mi);
298 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
299 GLfloat spcN1x,spcN1y,spcN2x,spcN2y;
300 GLfloat spcC1x,spcC1y,spcC2x,spcC2y;
301 int wire = MI_IS_WIREFRAME(mi);
304 if (!tb->glx_context)
306 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tb->glx_context));
307 mi->polygon_count = 0;
309 generateNewBlock(mi);
311 if (rotate && (!tb->button_down_p)) { tb->rotation += tb->rotateSpeed; }
312 if (tb->rotation>=360) { tb->rotation=tb->rotation-360; }
314 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* clear current */
315 glLoadIdentity(); /* resets directions, do it every time ! */
316 glRotatef(current_device_rotation(), 0, 0, 1);
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 */
327 /* Rotate the scene around a point that's a little higher up. */
328 glTranslatef (0, 0, -5);
329 gltrackball_rotate (tb->trackball);
330 glTranslatef (0, 0, 5);
332 /* rotate the world */
333 glRotatef(tb->rotation, 0.0, 0.0, 1.0);
335 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
337 GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
338 int o = (int) current_device_rotation();
339 if (o != 0 && o != 180 && o != -180)
340 glScalef (1/h, 1/h, 1/h);
344 llCurrent = tb->blockNodeRoot;
347 glTranslatef(0.0-(tb->carpetWidth/2),0.0-(tb->carpetLength/2),0.0);
348 glCallList(tb->carpet);
349 mi->polygon_count += tb->carpet_polys;
350 glTranslatef(0.0+(tb->carpetWidth/2),0.0+(tb->carpetLength/2),0.0);
351 glTranslatef(0.0,0.0,-0.55);
353 tb->highestFalling=0;
354 while (llCurrent != NULL) { /* for each block */
355 glPushMatrix(); /* save state */
357 switch (llCurrent->color) {
407 if (wire) { glColor3fv(color); }
408 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
410 if (llCurrent->falling==1) {
415 if (llCurrent->height>tb->highestFalling) {tb->highestFalling=llCurrent->height;}
416 /* all blocks fall at the same rate to avoid mid air collisions */
417 llCurrent->height-=tb->dropSpeed;
418 if (llCurrent->height<=0) {
419 llCurrent->falling=0;
420 if (tb->highest==0) {
421 tb->highest+=blockHeight;
424 if ( (llCurrent->height<=tb->highest+1) && (llCurrent->falling==1) ) {
425 /* check for collision */
426 llNode = tb->blockNodeRoot;
427 spcC1x = llCurrent->x;
428 spcC1y = llCurrent->y;
429 switch(llCurrent->rotation) {
430 case getOrientation(0):
434 case getOrientation(1):
438 case getOrientation(2):
442 case getOrientation(3):
447 while (llNode != NULL) {
448 if ( (llNode->falling==0) && (llCurrent->falling==1) ) {
451 switch(llNode->rotation) {
452 case getOrientation(0):
456 case getOrientation(1):
460 case getOrientation(2):
464 case getOrientation(3):
470 ( (spcC1x==spcN1x) && (spcC1y==spcN1y) ) ||
471 ( (spcC1x==spcN2x) && (spcC1y==spcN2y) ) ||
472 ( (spcC2x==spcN2x) && (spcC2y==spcN2y) ) ||
473 ( (spcC2x==spcN1x) && (spcC2y==spcN1y) )
475 if ( fabs(llCurrent->height-(llNode->height+blockHeight)) <= TOLERANCE) {
477 llCurrent->falling=0;
478 llCurrent->height=llNode->height+blockHeight; /* if this is missing then small errors build up until the model fails */
479 if ( fabs(llCurrent->height-tb->highest) <= TOLERANCE+blockHeight ) {
480 tb->highest+=blockHeight;
489 /* set location in space */
490 glTranslatef(llCurrent->x,llCurrent->y,-llCurrent->height);
492 glRotatef(llCurrent->rotation, 0.0f, 0.0f, 1.0f);
493 if ((tb->followMode==0) && (llCurrent->next==NULL)) {
494 tb->blockNodeFollow = llCurrent;
497 llCurrent = llCurrent->next;
499 glCallList(tb->block);
500 mi->polygon_count += tb->block_polys;
501 glPopMatrix(); /* restore state */
503 if (mi->fps_p) do_fps (mi);
506 if (tb->highest>(5*tb->maxFalling)) { drawCarpet=False; }
507 glXSwapBuffers(dpy, window);
512 /* camera is in follow mode, work out where we should be looking */
513 static void followBlock(ModeInfo *mi)
515 GLfloat xLen,yLen,cx,cy,rangle,xTarget,yTarget;
516 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
518 if ((tb->blockNodeFollow!=NULL) && (tb->followMode==1)){
520 if (tb->highest>tb->eyeLine) { tb->eyeLine+= ((tb->highest-tb->eyeLine)/100); }
521 /*tb->blockNodeFollow->color=1; only noticable if you set the colors to 1 */
523 if (tb->blockNodeFollow->height > tb->eyeZ) { tb->eyeZ+= ((tb->blockNodeFollow->height - tb->eyeZ)/100); }
524 if (tb->blockNodeFollow->height < tb->eyeZ) { tb->eyeZ-= ((tb->eyeZ - tb->blockNodeFollow->height)/100); }
527 /* when the scene is rotated we need to know where the block is in the 2 dimensional coordinates of the carpet area
528 (see http://www.jumpstation.co.uk/rotation/)
531 if (tb->followRadius==0) {
532 xLen = tb->blockNodeFollow->x-cx;
533 yLen = tb->blockNodeFollow->y-cy;
534 tb->followRadius=sqrt( (xLen*xLen) + (yLen*yLen) );
535 tb->followAngle = (180/M_PI) * asin(xLen/tb->followRadius);
536 tb->followAngle = quadrantCorrection(tb->followAngle,(int)cx,(int)cy,(int)tb->blockNodeFollow->x,(int)tb->blockNodeFollow->y);
538 rangle = (tb->followAngle+tb->rotation) * M_PI /180;
539 xTarget = cos(rangle) * tb->followRadius + cx;
540 yTarget = sin(rangle) * tb->followRadius + cy;
541 if (tb->followAngle>360) { tb->followAngle=tb->followAngle-360; }
543 if (xTarget < tb->eyeX) { tb->eyeX-= ((tb->eyeX - xTarget)/100); }
544 if (xTarget > tb->eyeX) { tb->eyeX+= ((xTarget - tb->eyeX)/100); }
546 if (yTarget < tb->eyeY) { tb->eyeY-= ((tb->eyeY - yTarget)/100); }
547 if (yTarget > tb->eyeY) { tb->eyeY+= ((yTarget - tb->eyeY)/100); }
548 if (!tb->blockNodeFollow->falling) {
550 /*tb->blockNodeFollow->color=2; only noticable if you set the colors to 1 */
554 gluLookAt(camX, camY, camZ-tb->eyeLine, tb->eyeX, tb->eyeY, -tb->eyeZ,-1.0,0.0,0.0);
557 /* each quater of the circle has to be adjusted for */
558 static double quadrantCorrection(double angle,int cx,int cy,int x,int y)
560 if ((x>=cx) && (y>=cy)) {
561 angle += (90-(angle-90) * 2);
562 } else if ((x>=cx) && (y<=cy)) {
564 } else if ((x<=cx) && (y<=cy)) {
566 } else if ((x<=cx) && (y>=cy)) {
567 angle += (90-(angle-90) * 2);
572 /* if random chance then create a new falling block */
573 static void generateNewBlock(ModeInfo *mi)
575 NODE *llCurrent, *llTail;
576 GLfloat startOffx, startOffy;
577 int endOffx, endOffy;
578 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
579 if ( ((random() % spawn) == 1) && (tb->highestFalling<getHeight((tb->plusheight-blockHeight)+tb->highest)) ) {
584 tb->numFallingBlocks++;
585 llTail = tb->blockNodeRoot;
586 if (llTail == NULL) {
587 llCurrent = ((NODE*) malloc(sizeof(NODE)));
588 if (!llCurrent) abort();
590 tb->blockNodeRoot = llCurrent;
592 if (tb->numFallingBlocks>=tb->maxFalling) {
594 llCurrent=llTail->next;
595 tb->blockNodeRoot=llCurrent->next;
597 llCurrent = ((NODE*) malloc(sizeof(NODE)));
598 if (!llCurrent) abort();
600 while (llTail->next != NULL) { llTail = llTail->next; } /* find last item in list */
602 llCurrent->falling=1;
603 llCurrent->rotation=getOrientation(random() % 4);
604 if (llCurrent->rotation==getOrientation(0)) {
609 } else if (llCurrent->rotation==getOrientation(1)) {
614 } else if (llCurrent->rotation==getOrientation(2)) {
619 } else if (llCurrent->rotation==getOrientation(3)) {
626 llCurrent->x=(startOffx-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffx) );
627 llCurrent->y=(startOffy-(tb->carpetLength/2)) + getLocation(random() % ((tb->carpetLength/2)+endOffy) );
628 llCurrent->color=(random() % maxColors);
629 llCurrent->height=getHeight(tb->plusheight+tb->highest);
630 if (tb->numFallingBlocks>=tb->maxFalling) {
631 tb->numFallingBlocks--;
632 tb->numFallingBlocks--;
634 llTail->next = llCurrent;
641 /* called at init this creates the 'carpet' display list item */
642 static void buildCarpet(ModeInfo *mi)
646 int wire = MI_IS_WIREFRAME(mi);
647 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
652 tb->carpet=glGenLists(1); /* only one */
653 glNewList(tb->carpet,GL_COMPILE);
655 glPushMatrix(); /* save state */
658 if (wire) { glColor3fv(color); }
659 else { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); }
660 /* draw carpet plane */
661 glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
663 glNormal3f( 0, 0, -1 );
664 glVertex3f(0.0,0.0,0.0);
665 glVertex3f(x,0.0,0.0);
667 glVertex3f(0.0,y,0.0);
670 /* add edge pieces */
672 glNormal3f( 0, -1, 0 );
673 glVertex3f(0.0,0.0,0.0);
674 glVertex3f(x,0.0,0.0);
675 glVertex3f(x,0,singleThick);
676 glVertex3f(0.0,0,singleThick);
679 glNormal3f( -1, 0, 0 );
680 glVertex3f(0.0,0.0,0.0);
682 glVertex3f(0,y,singleThick);
683 glVertex3f(0.0,0,singleThick);
686 glNormal3f( 1, 0, 0 );
687 glVertex3f(x,0.0,0.0);
689 glVertex3f(x,y,singleThick);
690 glVertex3f(x,0,singleThick);
693 glNormal3f( 0, 1, 0 );
696 glVertex3f(x,y,singleThick);
697 glVertex3f(0,y,singleThick);
703 glTranslatef(0.5f,0.5f,-.25); /* move to the cylinder center */
705 glPushMatrix(); /* save state */
707 tb->carpet_polys += tube(0, 0, -0.1,
710 tb->resolution, True, True,
712 glRotatef(180, 0.0f, 1.0f, 0.0f); /* they are upside down */
713 glRotatef(180, 0.0f, 1.0f, 0.0f); /* recover */
714 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
716 glPopMatrix(); /* save state */
717 glTranslatef(1.0f,0.0f,0.0f); /* reset */
720 glPopMatrix(); /* restore state */
724 /* using the verticies arrays builds the plane, now with normals */
725 static void polygonPlane(int wire, int a, int b, int c , int d, int i)
727 GLfloat topBlockNormals[5][3] = { {0,0,-1}, {0,1,0}, {1,0,0}, {0,0,1}, {0,-1,0} };
728 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} };
729 glBegin( wire ? GL_LINE_LOOP : GL_POLYGON);
730 glNormal3fv(topBlockNormals[i] );
731 glVertex3fv(topBlockVertices[a]);
732 glVertex3fv(topBlockVertices[b]);
733 glVertex3fv(topBlockVertices[c]);
734 glVertex3fv(topBlockVertices[d]);
738 /* called at init this creates the 'block' display list item */
739 /* the spheres came about originaly as quick way to test the directional lighting/normals */
740 static void buildBlock(ModeInfo *mi)
743 int wire = MI_IS_WIREFRAME(mi);
744 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
745 tb->block=glGenLists(1); /* only one */
746 glNewList(tb->block,GL_COMPILE);
748 glPushMatrix(); /* save state */
749 glRotatef(90, 0.0f, 1.0f, 0.0f);
751 polygonPlane(wire, 0,3,2,1,0); tb->block_polys++;
752 polygonPlane(wire, 2,3,7,6,1); tb->block_polys++;
753 polygonPlane(wire, 1,2,6,5,2); tb->block_polys++;
754 polygonPlane(wire, 4,5,6,7,3); tb->block_polys++;
755 polygonPlane(wire, 0,1,5,4,4); tb->block_polys++;
758 /* draw 8 cylinders each with a disk cap */
759 glRotatef(90, 0.0f, 1.0f, 0.0f); /* 'aim' the pointer ready for the cylinder */
760 glTranslatef(0.5f,0.5f,0.99f); /* move to the cylinder center */
763 tb->block_polys += tube(0, 0, 0,
766 tb->resolution, True, True,
768 glTranslatef(0.0f,0.0f,0.25f); /* move to the cylinder cap */
769 glTranslatef(0.0f,0.0f,-0.25f); /* move back from the cylinder cap */
771 glTranslatef(0.0f,-1.0f,0.0f); /* move to the next cylinder center (forward) */
773 glTranslatef(0.0f,1.0f,0.0f); /* move to the next cylinder center (backward) */
776 glTranslatef(-1.0f,1.0f,0.0f); /* move to the cylinder center */
779 /* 3 cylinders on the underside */
780 glTranslatef(1.5f,-2.5f,-1.5f); /* move to the center, under the top of the brick */
783 tb->block_polys += tube(0, 0, 0.1,
786 tb->resolution, True, True, wire);
787 glTranslatef(0.0f,-1.0f,0.0f); /* move to the center */
790 glPopMatrix(); /* restore state */
795 rip off of the builBlock() function creating the GL compilied pointer "block" but only creates two spheres.
796 spheres are created with unit_sphere from spheres.h to allow wire frame
798 static void buildBlobBlock(ModeInfo *mi)
800 int wire = MI_IS_WIREFRAME(mi);
801 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
802 tb->block=glGenLists(1); /* only one */
803 glNewList(tb->block,GL_COMPILE);
805 glScalef(1.4,1.4,1.4);
806 unit_sphere (tb->resolution/2,tb->resolution, wire);
808 glTranslatef(0.0f,-2.0f,0.0f);
809 glScalef(1.4,1.4,1.4);
810 unit_sphere (tb->resolution/2,tb->resolution, wire);
815 /* handle input events or not if daemon running the show */
817 topBlock_handle_event (ModeInfo *mi, XEvent *event)
819 topBlockSTATE *tb = &tbs[MI_SCREEN(mi)];
821 if (gltrackball_event_handler (event, tb->trackball,
822 MI_WIDTH (mi), MI_HEIGHT (mi),
825 else if (event->xany.type == KeyPress) {
828 XLookupString (&event->xkey, &c, 1, &keysym, 0);
832 } else if (c == 'z') {
835 } else if (c == 's') {
838 } else if (c == 'x') {
841 } else if (c == 'd') {
844 } else if (c == 'c') {
847 } else if (c == 'f') {
850 } else if (c == 'v') {
853 } else if (c == 'g') {
856 } else if (c == 'b') {
859 } else if (c == 'h') {
862 } else if (c == 'n') {
865 } else if (c == 'r') {
874 /* this is tha main change for v5 compatability and acompanying ENTRYPOINTS */
875 XSCREENSAVER_MODULE_2 ("TopBlock", topblock, topBlock)