• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************
2  * Title:   GLBoing
3  * Desc:    Tribute to Amiga Boing.
4  * Author:  Jim Brooks  <gfx@jimbrooks.org>
5  *          Original Amiga authors were R.J. Mical and Dale Luck.
6  *          GLFW conversion by Marcus Geelnard
7  * Notes:   - 360' = 2*PI [radian]
8  *
9  *          - Distances between objects are created by doing a relative
10  *            Z translations.
11  *
12  *          - Although OpenGL enticingly supports alpha-blending,
13  *            the shadow of the original Boing didn't affect the color
14  *            of the grid.
15  *
16  *          - [Marcus] Changed timing scheme from interval driven to frame-
17  *            time based animation steps (which results in much smoother
18  *            movement)
19  *
20  * History of Amiga Boing:
21  *
22  * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in
23  * 1985. According to legend, it was written ad-hoc in one night by
24  * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast
25  * and smooth, attendees did not believe the Amiga prototype was really doing
26  * the rendering. Suspecting a trick, they began looking around the booth for
27  * a hidden computer or VCR.
28  *****************************************************************************/
29 
30 #if defined(_MSC_VER)
31  // Make MS math.h define M_PI
32  #define _USE_MATH_DEFINES
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <math.h>
38 
39 #include <glad/glad.h>
40 #include <GLFW/glfw3.h>
41 
42 #include <linmath.h>
43 
44 
45 /*****************************************************************************
46  * Various declarations and macros
47  *****************************************************************************/
48 
49 /* Prototypes */
50 void init( void );
51 void display( void );
52 void reshape( GLFWwindow* window, int w, int h );
53 void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods );
54 void mouse_button_callback( GLFWwindow* window, int button, int action, int mods );
55 void cursor_position_callback( GLFWwindow* window, double x, double y );
56 void DrawBoingBall( void );
57 void BounceBall( double dt );
58 void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
59 void DrawGrid( void );
60 
61 #define RADIUS           70.f
62 #define STEP_LONGITUDE   22.5f                   /* 22.5 makes 8 bands like original Boing */
63 #define STEP_LATITUDE    22.5f
64 
65 #define DIST_BALL       (RADIUS * 2.f + RADIUS * 0.1f)
66 
67 #define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
68 #define GRID_SIZE       (RADIUS * 4.5f)          /* length (width) of grid */
69 #define BOUNCE_HEIGHT   (RADIUS * 2.1f)
70 #define BOUNCE_WIDTH    (RADIUS * 2.1f)
71 
72 #define SHADOW_OFFSET_X -20.f
73 #define SHADOW_OFFSET_Y  10.f
74 #define SHADOW_OFFSET_Z   0.f
75 
76 #define WALL_L_OFFSET   0.f
77 #define WALL_R_OFFSET   5.f
78 
79 /* Animation speed (50.0 mimics the original GLUT demo speed) */
80 #define ANIMATION_SPEED 50.f
81 
82 /* Maximum allowed delta time per physics iteration */
83 #define MAX_DELTA_T 0.02f
84 
85 /* Draw ball, or its shadow */
86 typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
87 
88 /* Vertex type */
89 typedef struct {float x; float y; float z;} vertex_t;
90 
91 /* Global vars */
92 int windowed_xpos, windowed_ypos, windowed_width, windowed_height;
93 int width, height;
94 GLfloat deg_rot_y       = 0.f;
95 GLfloat deg_rot_y_inc   = 2.f;
96 int override_pos        = GLFW_FALSE;
97 GLfloat cursor_x        = 0.f;
98 GLfloat cursor_y        = 0.f;
99 GLfloat ball_x          = -RADIUS;
100 GLfloat ball_y          = -RADIUS;
101 GLfloat ball_x_inc      = 1.f;
102 GLfloat ball_y_inc      = 2.f;
103 DRAW_BALL_ENUM drawBallHow;
104 double  t;
105 double  t_old = 0.f;
106 double  dt;
107 
108 /* Random number generator */
109 #ifndef RAND_MAX
110  #define RAND_MAX 4095
111 #endif
112 
113 
114 /*****************************************************************************
115  * Truncate a degree.
116  *****************************************************************************/
TruncateDeg(GLfloat deg)117 GLfloat TruncateDeg( GLfloat deg )
118 {
119    if ( deg >= 360.f )
120       return (deg - 360.f);
121    else
122       return deg;
123 }
124 
125 /*****************************************************************************
126  * Convert a degree (360-based) into a radian.
127  * 360' = 2 * PI
128  *****************************************************************************/
deg2rad(double deg)129 double deg2rad( double deg )
130 {
131    return deg / 360 * (2 * M_PI);
132 }
133 
134 /*****************************************************************************
135  * 360' sin().
136  *****************************************************************************/
sin_deg(double deg)137 double sin_deg( double deg )
138 {
139    return sin( deg2rad( deg ) );
140 }
141 
142 /*****************************************************************************
143  * 360' cos().
144  *****************************************************************************/
cos_deg(double deg)145 double cos_deg( double deg )
146 {
147    return cos( deg2rad( deg ) );
148 }
149 
150 /*****************************************************************************
151  * Compute a cross product (for a normal vector).
152  *
153  * c = a x b
154  *****************************************************************************/
CrossProduct(vertex_t a,vertex_t b,vertex_t c,vertex_t * n)155 void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
156 {
157    GLfloat u1, u2, u3;
158    GLfloat v1, v2, v3;
159 
160    u1 = b.x - a.x;
161    u2 = b.y - a.y;
162    u3 = b.y - a.z;
163 
164    v1 = c.x - a.x;
165    v2 = c.y - a.y;
166    v3 = c.z - a.z;
167 
168    n->x = u2 * v3 - v2 * v3;
169    n->y = u3 * v1 - v3 * u1;
170    n->z = u1 * v2 - v1 * u2;
171 }
172 
173 
174 #define BOING_DEBUG 0
175 
176 
177 /*****************************************************************************
178  * init()
179  *****************************************************************************/
init(void)180 void init( void )
181 {
182    /*
183     * Clear background.
184     */
185    glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
186 
187    glShadeModel( GL_FLAT );
188 }
189 
190 
191 /*****************************************************************************
192  * display()
193  *****************************************************************************/
display(void)194 void display(void)
195 {
196    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
197    glPushMatrix();
198 
199    drawBallHow = DRAW_BALL_SHADOW;
200    DrawBoingBall();
201 
202    DrawGrid();
203 
204    drawBallHow = DRAW_BALL;
205    DrawBoingBall();
206 
207    glPopMatrix();
208    glFlush();
209 }
210 
211 
212 /*****************************************************************************
213  * reshape()
214  *****************************************************************************/
reshape(GLFWwindow * window,int w,int h)215 void reshape( GLFWwindow* window, int w, int h )
216 {
217    mat4x4 projection, view;
218 
219    glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
220 
221    glMatrixMode( GL_PROJECTION );
222    mat4x4_perspective( projection,
223                        2.f * (float) atan2( RADIUS, 200.f ),
224                        (float)w / (float)h,
225                        1.f, VIEW_SCENE_DIST );
226    glLoadMatrixf((const GLfloat*) projection);
227 
228    glMatrixMode( GL_MODELVIEW );
229    {
230       vec3 eye = { 0.f, 0.f, VIEW_SCENE_DIST };
231       vec3 center = { 0.f, 0.f, 0.f };
232       vec3 up = { 0.f, -1.f, 0.f };
233       mat4x4_look_at( view, eye, center, up );
234    }
235    glLoadMatrixf((const GLfloat*) view);
236 }
237 
key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)238 void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods )
239 {
240     if (action != GLFW_PRESS)
241         return;
242 
243     if (key == GLFW_KEY_ESCAPE && mods == 0)
244         glfwSetWindowShouldClose(window, GLFW_TRUE);
245     if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) ||
246         (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT))
247     {
248         if (glfwGetWindowMonitor(window))
249         {
250             glfwSetWindowMonitor(window, NULL,
251                                  windowed_xpos, windowed_ypos,
252                                  windowed_width, windowed_height, 0);
253         }
254         else
255         {
256             GLFWmonitor* monitor = glfwGetPrimaryMonitor();
257             if (monitor)
258             {
259                 const GLFWvidmode* mode = glfwGetVideoMode(monitor);
260                 glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
261                 glfwGetWindowSize(window, &windowed_width, &windowed_height);
262                 glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
263             }
264         }
265     }
266 }
267 
set_ball_pos(GLfloat x,GLfloat y)268 static void set_ball_pos ( GLfloat x, GLfloat y )
269 {
270    ball_x = (width / 2) - x;
271    ball_y = y - (height / 2);
272 }
273 
mouse_button_callback(GLFWwindow * window,int button,int action,int mods)274 void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
275 {
276    if (button != GLFW_MOUSE_BUTTON_LEFT)
277       return;
278 
279    if (action == GLFW_PRESS)
280    {
281       override_pos = GLFW_TRUE;
282       set_ball_pos(cursor_x, cursor_y);
283    }
284    else
285    {
286       override_pos = GLFW_FALSE;
287    }
288 }
289 
cursor_position_callback(GLFWwindow * window,double x,double y)290 void cursor_position_callback( GLFWwindow* window, double x, double y )
291 {
292    cursor_x = (float) x;
293    cursor_y = (float) y;
294 
295    if ( override_pos )
296       set_ball_pos(cursor_x, cursor_y);
297 }
298 
299 /*****************************************************************************
300  * Draw the Boing ball.
301  *
302  * The Boing ball is sphere in which each facet is a rectangle.
303  * Facet colors alternate between red and white.
304  * The ball is built by stacking latitudinal circles.  Each circle is composed
305  * of a widely-separated set of points, so that each facet is noticably large.
306  *****************************************************************************/
DrawBoingBall(void)307 void DrawBoingBall( void )
308 {
309    GLfloat lon_deg;     /* degree of longitude */
310    double dt_total, dt2;
311 
312    glPushMatrix();
313    glMatrixMode( GL_MODELVIEW );
314 
315   /*
316    * Another relative Z translation to separate objects.
317    */
318    glTranslatef( 0.0, 0.0, DIST_BALL );
319 
320    /* Update ball position and rotation (iterate if necessary) */
321    dt_total = dt;
322    while( dt_total > 0.0 )
323    {
324        dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
325        dt_total -= dt2;
326        BounceBall( dt2 );
327        deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
328    }
329 
330    /* Set ball position */
331    glTranslatef( ball_x, ball_y, 0.0 );
332 
333   /*
334    * Offset the shadow.
335    */
336    if ( drawBallHow == DRAW_BALL_SHADOW )
337    {
338       glTranslatef( SHADOW_OFFSET_X,
339                     SHADOW_OFFSET_Y,
340                     SHADOW_OFFSET_Z );
341    }
342 
343   /*
344    * Tilt the ball.
345    */
346    glRotatef( -20.0, 0.0, 0.0, 1.0 );
347 
348   /*
349    * Continually rotate ball around Y axis.
350    */
351    glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
352 
353   /*
354    * Set OpenGL state for Boing ball.
355    */
356    glCullFace( GL_FRONT );
357    glEnable( GL_CULL_FACE );
358    glEnable( GL_NORMALIZE );
359 
360   /*
361    * Build a faceted latitude slice of the Boing ball,
362    * stepping same-sized vertical bands of the sphere.
363    */
364    for ( lon_deg = 0;
365          lon_deg < 180;
366          lon_deg += STEP_LONGITUDE )
367    {
368      /*
369       * Draw a latitude circle at this longitude.
370       */
371       DrawBoingBallBand( lon_deg,
372                          lon_deg + STEP_LONGITUDE );
373    }
374 
375    glPopMatrix();
376 
377    return;
378 }
379 
380 
381 /*****************************************************************************
382  * Bounce the ball.
383  *****************************************************************************/
BounceBall(double delta_t)384 void BounceBall( double delta_t )
385 {
386    GLfloat sign;
387    GLfloat deg;
388 
389    if ( override_pos )
390      return;
391 
392    /* Bounce on walls */
393    if ( ball_x >  (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
394    {
395       ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
396       deg_rot_y_inc = -deg_rot_y_inc;
397    }
398    if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
399    {
400       ball_x_inc =  0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
401       deg_rot_y_inc = -deg_rot_y_inc;
402    }
403 
404    /* Bounce on floor / roof */
405    if ( ball_y >  BOUNCE_HEIGHT/2      )
406    {
407       ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
408    }
409    if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
410    {
411       ball_y_inc =  0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
412    }
413 
414    /* Update ball position */
415    ball_x += ball_x_inc * ((float)delta_t*ANIMATION_SPEED);
416    ball_y += ball_y_inc * ((float)delta_t*ANIMATION_SPEED);
417 
418   /*
419    * Simulate the effects of gravity on Y movement.
420    */
421    if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
422 
423    deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
424    if ( deg > 80 ) deg = 80;
425    if ( deg < 10 ) deg = 10;
426 
427    ball_y_inc = sign * 4.f * (float) sin_deg( deg );
428 }
429 
430 
431 /*****************************************************************************
432  * Draw a faceted latitude band of the Boing ball.
433  *
434  * Parms:   long_lo, long_hi
435  *          Low and high longitudes of slice, resp.
436  *****************************************************************************/
DrawBoingBallBand(GLfloat long_lo,GLfloat long_hi)437 void DrawBoingBallBand( GLfloat long_lo,
438                         GLfloat long_hi )
439 {
440    vertex_t vert_ne;            /* "ne" means south-east, so on */
441    vertex_t vert_nw;
442    vertex_t vert_sw;
443    vertex_t vert_se;
444    vertex_t vert_norm;
445    GLfloat  lat_deg;
446    static int colorToggle = 0;
447 
448   /*
449    * Iterate thru the points of a latitude circle.
450    * A latitude circle is a 2D set of X,Z points.
451    */
452    for ( lat_deg = 0;
453          lat_deg <= (360 - STEP_LATITUDE);
454          lat_deg += STEP_LATITUDE )
455    {
456      /*
457       * Color this polygon with red or white.
458       */
459       if ( colorToggle )
460          glColor3f( 0.8f, 0.1f, 0.1f );
461       else
462          glColor3f( 0.95f, 0.95f, 0.95f );
463 #if 0
464       if ( lat_deg >= 180 )
465          if ( colorToggle )
466             glColor3f( 0.1f, 0.8f, 0.1f );
467          else
468             glColor3f( 0.5f, 0.5f, 0.95f );
469 #endif
470       colorToggle = ! colorToggle;
471 
472      /*
473       * Change color if drawing shadow.
474       */
475       if ( drawBallHow == DRAW_BALL_SHADOW )
476          glColor3f( 0.35f, 0.35f, 0.35f );
477 
478      /*
479       * Assign each Y.
480       */
481       vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
482       vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
483 
484      /*
485       * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
486       * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
487       * while long=90 (sin(90)=1) is at equator.
488       */
489       vert_ne.x = (float) cos_deg( lat_deg                 ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
490       vert_se.x = (float) cos_deg( lat_deg                 ) * (RADIUS * (float) sin_deg( long_lo                  ));
491       vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
492       vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo                  ));
493 
494       vert_ne.z = (float) sin_deg( lat_deg                 ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
495       vert_se.z = (float) sin_deg( lat_deg                 ) * (RADIUS * (float) sin_deg( long_lo                  ));
496       vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
497       vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo                  ));
498 
499      /*
500       * Draw the facet.
501       */
502       glBegin( GL_POLYGON );
503 
504       CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
505       glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
506 
507       glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
508       glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
509       glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
510       glVertex3f( vert_se.x, vert_se.y, vert_se.z );
511 
512       glEnd();
513 
514 #if BOING_DEBUG
515       printf( "----------------------------------------------------------- \n" );
516       printf( "lat = %f  long_lo = %f  long_hi = %f \n", lat_deg, long_lo, long_hi );
517       printf( "vert_ne  x = %.8f  y = %.8f  z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
518       printf( "vert_nw  x = %.8f  y = %.8f  z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
519       printf( "vert_se  x = %.8f  y = %.8f  z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
520       printf( "vert_sw  x = %.8f  y = %.8f  z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
521 #endif
522 
523    }
524 
525   /*
526    * Toggle color so that next band will opposite red/white colors than this one.
527    */
528    colorToggle = ! colorToggle;
529 
530   /*
531    * This circular band is done.
532    */
533    return;
534 }
535 
536 
537 /*****************************************************************************
538  * Draw the purple grid of lines, behind the Boing ball.
539  * When the Workbench is dropped to the bottom, Boing shows 12 rows.
540  *****************************************************************************/
DrawGrid(void)541 void DrawGrid( void )
542 {
543    int              row, col;
544    const int        rowTotal    = 12;                   /* must be divisible by 2 */
545    const int        colTotal    = rowTotal;             /* must be same as rowTotal */
546    const GLfloat    widthLine   = 2.0;                  /* should be divisible by 2 */
547    const GLfloat    sizeCell    = GRID_SIZE / rowTotal;
548    const GLfloat    z_offset    = -40.0;
549    GLfloat          xl, xr;
550    GLfloat          yt, yb;
551 
552    glPushMatrix();
553    glDisable( GL_CULL_FACE );
554 
555   /*
556    * Another relative Z translation to separate objects.
557    */
558    glTranslatef( 0.0, 0.0, DIST_BALL );
559 
560   /*
561    * Draw vertical lines (as skinny 3D rectangles).
562    */
563    for ( col = 0; col <= colTotal; col++ )
564    {
565      /*
566       * Compute co-ords of line.
567       */
568       xl = -GRID_SIZE / 2 + col * sizeCell;
569       xr = xl + widthLine;
570 
571       yt =  GRID_SIZE / 2;
572       yb = -GRID_SIZE / 2 - widthLine;
573 
574       glBegin( GL_POLYGON );
575 
576       glColor3f( 0.6f, 0.1f, 0.6f );               /* purple */
577 
578       glVertex3f( xr, yt, z_offset );       /* NE */
579       glVertex3f( xl, yt, z_offset );       /* NW */
580       glVertex3f( xl, yb, z_offset );       /* SW */
581       glVertex3f( xr, yb, z_offset );       /* SE */
582 
583       glEnd();
584    }
585 
586   /*
587    * Draw horizontal lines (as skinny 3D rectangles).
588    */
589    for ( row = 0; row <= rowTotal; row++ )
590    {
591      /*
592       * Compute co-ords of line.
593       */
594       yt = GRID_SIZE / 2 - row * sizeCell;
595       yb = yt - widthLine;
596 
597       xl = -GRID_SIZE / 2;
598       xr =  GRID_SIZE / 2 + widthLine;
599 
600       glBegin( GL_POLYGON );
601 
602       glColor3f( 0.6f, 0.1f, 0.6f );               /* purple */
603 
604       glVertex3f( xr, yt, z_offset );       /* NE */
605       glVertex3f( xl, yt, z_offset );       /* NW */
606       glVertex3f( xl, yb, z_offset );       /* SW */
607       glVertex3f( xr, yb, z_offset );       /* SE */
608 
609       glEnd();
610    }
611 
612    glPopMatrix();
613 
614    return;
615 }
616 
617 
618 /*======================================================================*
619  * main()
620  *======================================================================*/
621 
main(void)622 int main( void )
623 {
624    GLFWwindow* window;
625 
626    /* Init GLFW */
627    if( !glfwInit() )
628       exit( EXIT_FAILURE );
629 
630    window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL );
631    if (!window)
632    {
633        glfwTerminate();
634        exit( EXIT_FAILURE );
635    }
636 
637    glfwSetWindowAspectRatio(window, 1, 1);
638 
639    glfwSetFramebufferSizeCallback(window, reshape);
640    glfwSetKeyCallback(window, key_callback);
641    glfwSetMouseButtonCallback(window, mouse_button_callback);
642    glfwSetCursorPosCallback(window, cursor_position_callback);
643 
644    glfwMakeContextCurrent(window);
645    gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
646    glfwSwapInterval( 1 );
647 
648    glfwGetFramebufferSize(window, &width, &height);
649    reshape(window, width, height);
650 
651    glfwSetTime( 0.0 );
652 
653    init();
654 
655    /* Main loop */
656    for (;;)
657    {
658        /* Timing */
659        t = glfwGetTime();
660        dt = t - t_old;
661        t_old = t;
662 
663        /* Draw one frame */
664        display();
665 
666        /* Swap buffers */
667        glfwSwapBuffers(window);
668        glfwPollEvents();
669 
670        /* Check if we are still running */
671        if (glfwWindowShouldClose(window))
672            break;
673    }
674 
675    glfwTerminate();
676    exit( EXIT_SUCCESS );
677 }
678 
679