GENEXPLD.C
/******************************Module*Header*******************************\ 
* Module Name: genexpld.c 
* 
* The Explode style of the 3D Flying Objects screen saver. 
* 
* Simulation of a sphere that occasionally explodes. 
* 
* Copyright (c) 1994 Microsoft Corporation 
* 
\**************************************************************************/ 
 
#include <windows.h> 
#include <GL\gl.h> 
#include <stdlib.h> 
#include <string.h> 
#include <math.h> 
#include "ss3dfo.h" 
 
#define RADIUS         0.3 
#define STEPS    30 
#define MAXPREC20 
 
static MATRIX *faceMat; 
static float *xstep; 
static float *ystep; 
static float *zstep; 
static float *xrot; 
static float *yrot; 
static float *zrot; 
static MESH explodeMesh; 
static int iPrec = 10; 
 
static BOOL bOpenGL11; 
 
// Data type accepted by glInterleavedArrays 
typedef struct _POINT_N3F_V3F { 
    POINT3D normal; 
    POINT3D vertex; 
} POINT_N3F_V3F; 
 
static POINT_N3F_V3F *pN3V3; 
 
static GLfloat matl1Diffuse[] = {1.0f, 0.8f, 0.0f, 1.0f}; 
static GLfloat matl2Diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f}; 
static GLfloat matlSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f}; 
static GLfloat light0Pos[] = {100.0f, 100.0f, 100.0f, 0.0f}; 
 
void genExplode() 
{ 
    int i; 
    POINT3D circle[MAXPREC+1]; 
    double angle; 
    double step = -PI / (float)(iPrec - 1); 
    double start = PI / 2.0; 
     
    for (i = 0, angle = start; i < iPrec; i++, angle += step) { 
        circle[i].x = (float) (RADIUS * cos(angle)); 
        circle[i].y = (float) (RADIUS * sin(angle)); 
        circle[i].z = 0.0f; 
    } 
 
    revolveSurface(&explodeMesh, circle, iPrec); 
 
    for (i = 0; i < explodeMesh.numFaces; i++) { 
        ss_matrixIdent(&faceMat[i]); 
        xstep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
        ystep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
        zstep[i] = (float)(((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
        xrot[i] = 0.0f; 
        yrot[i] = 0.0f; 
        zrot[i] = 0.0f; 
    } 
} 
 
void initExplodeScene() 
{ 
    iPrec = (int)(fTesselFact * 10.5); 
    if (iPrec < 5) 
        iPrec = 5; 
    if (iPrec > MAXPREC) 
        iPrec = MAXPREC; 
 
    faceMat = (MATRIX *)SaverAlloc((iPrec * iPrec) *  
     (4 * 4 * sizeof(float))); 
    xstep = SaverAlloc(iPrec * iPrec * sizeof(float)); 
    ystep = SaverAlloc(iPrec * iPrec * sizeof(float)); 
    zstep = SaverAlloc(iPrec * iPrec * sizeof(float)); 
    xrot = SaverAlloc(iPrec * iPrec * sizeof(float)); 
    yrot = SaverAlloc(iPrec * iPrec * sizeof(float)); 
    zrot = SaverAlloc(iPrec * iPrec * sizeof(float)); 
     
    genExplode(); 
 
    // Find out the OpenGL version that we are running on. 
    bOpenGL11 = ss_fOnGL11(); 
 
    // Setup the data arrays. 
    pN3V3 = SaverAlloc(explodeMesh.numFaces * 4 * sizeof(POINT_N3F_V3F)); 
 
    // If we are running on OpenGL 1.1, use the new vertex array functions. 
    if (bOpenGL11) { 
        glInterleavedArrays(GL_N3F_V3F, 0, pN3V3); 
    } 
 
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matl1Diffuse); 
    glMaterialfv(GL_FRONT, GL_SPECULAR, matlSpecular); 
    glMaterialf(GL_FRONT, GL_SHININESS, 100.0f); 
 
    glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, matl2Diffuse); 
    glMaterialfv(GL_BACK, GL_SPECULAR, matlSpecular); 
    glMaterialf(GL_BACK, GL_SHININESS, 60.0f); 
 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glFrustum(-0.33, 0.33, -0.33, 0.33, 0.3, 3.0); 
 
    glTranslatef(0.0f, 0.0f, -1.5f); 
} 
 
 
void delExplodeScene() 
{ 
    delMesh(&explodeMesh); 
     
    SaverFree(faceMat); 
    SaverFree(xstep); 
    SaverFree(ystep); 
    SaverFree(zstep); 
    SaverFree(xrot); 
    SaverFree(yrot); 
    SaverFree(zrot); 
    SaverFree(pN3V3); 
} 
 
void updateExplodeScene(int flags) 
{ 
    static double mxrot = 0.0; 
    static double myrot = 0.0; 
    static double mzrot = 0.0; 
    static double mxrotInc = 0.0; 
    static double myrotInc = 0.1; 
    static double mzrotInc = 0.0; 
    static float maxR; 
    static float r = 0.0f; 
    static float rotZ = 0.0f; 
    static int count = 0; 
    static int direction = 1; 
    static int restCount = 0; 
    static float lightSpin = 0.0f; 
    static float spinDelta = 5.0f; 
    static int h = 0; 
    static RGBA color; 
    int i; 
    MFACE *faces; 
    POINT_N3F_V3F *pn3v3; 
 
 
    if( gbBounce ) { 
        // floating window bounced off an edge 
        if (mxrotInc) { 
            mxrotInc = 0.0; 
            myrotInc = 0.1; 
        } else if (myrotInc) { 
            myrotInc = 0.0; 
            mzrotInc = 0.1; 
        } else if (mzrotInc) { 
            mzrotInc = 0.0; 
            mxrotInc = 0.1; 
        } 
        gbBounce = FALSE; 
    } 
 
    mxrot += mxrotInc; 
    myrot += myrotInc; 
    mzrot += mzrotInc; 
 
    if (bColorCycle || h == 0) { 
        ss_HsvToRgb((float)h, 1.0f, 1.0f, &color); 
 
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, (GLfloat *) &color); 
 
        h++; 
        h %= 360; 
    } 
 
    glMatrixMode(GL_MODELVIEW); 
    glPushMatrix(); 
    glRotatef(-lightSpin, 0.0f, 1.0f, 0.0f); 
    glLightfv(GL_LIGHT0, GL_POSITION, light0Pos); 
    lightSpin += spinDelta; 
    if ((lightSpin > 90.0) || (lightSpin < 0.0)) 
        spinDelta = -spinDelta; 
    glPopMatrix(); 
 
    if (!bOpenGL11) { 
        glBegin(GL_QUADS); 
    } 
 
    for( 
        i = 0, faces = explodeMesh.faces, pn3v3 = pN3V3; 
        i < explodeMesh.numFaces; 
i++, faces++, pn3v3 += 4 
       ) { 
        int a, b, c, d; 
        int j; 
        POINT3D vector; 
         
        ss_matrixIdent(&faceMat[i]); 
        ss_matrixRotate(&faceMat[i], xrot[i], yrot[i], zrot[i]); 
 
        if (restCount) 
            ; 
        else { 
            xrot[i] += (xstep[i]); 
            yrot[i] += (ystep[i]); 
            zrot[i] += (zstep[i]); 
        }  
 
        a = faces->p[0]; 
        b = faces->p[1]; 
        c = faces->p[3]; 
        d = faces->p[2]; 
         
        memcpy(&pn3v3[0].vertex, (explodeMesh.pts + a), sizeof(POINT3D)); 
        memcpy(&pn3v3[1].vertex, (explodeMesh.pts + b), sizeof(POINT3D)); 
        memcpy(&pn3v3[2].vertex, (explodeMesh.pts + c), sizeof(POINT3D)); 
        memcpy(&pn3v3[3].vertex, (explodeMesh.pts + d), sizeof(POINT3D)); 
 
        vector.x = pn3v3[0].vertex.x; 
        vector.y = pn3v3[0].vertex.y; 
        vector.z = pn3v3[0].vertex.z; 
 
        for (j = 0; j < 4; j++) { 
            pn3v3[j].vertex.x -= vector.x; 
            pn3v3[j].vertex.y -= vector.y; 
            pn3v3[j].vertex.z -= vector.z; 
            ss_xformPoint((POINT3D *)&pn3v3[j].vertex, (POINT3D *)&pn3v3[j].vertex, &faceMat[i]); 
            pn3v3[j].vertex.x += vector.x + (vector.x * r); 
            pn3v3[j].vertex.y += vector.y + (vector.y * r); 
            pn3v3[j].vertex.z += vector.z + (vector.z * r); 
        } 
        if (bSmoothShading) { 
            memcpy(&pn3v3[0].normal, (explodeMesh.norms + a), sizeof(POINT3D)); 
            memcpy(&pn3v3[1].normal, (explodeMesh.norms + b), sizeof(POINT3D)); 
            memcpy(&pn3v3[2].normal, (explodeMesh.norms + c), sizeof(POINT3D)); 
            memcpy(&pn3v3[3].normal, (explodeMesh.norms + d), sizeof(POINT3D)); 
            
            for (j = 0; j < 4; j++) 
                ss_xformNorm((POINT3D *)&pn3v3[j].normal, (POINT3D *)&pn3v3[j].normal, &faceMat[i]); 
        } else {             
            memcpy(&pn3v3[0].normal, &faces->norm, sizeof(POINT3D)); 
            ss_xformNorm((POINT3D *)&pn3v3[0].normal, (POINT3D *)&pn3v3[0].normal, &faceMat[i]); 
            memcpy(&pn3v3[1].normal, &pn3v3[0].normal, sizeof(POINT3D)); 
            memcpy(&pn3v3[2].normal, &pn3v3[0].normal, sizeof(POINT3D)); 
            memcpy(&pn3v3[3].normal, &pn3v3[0].normal, sizeof(POINT3D)); 
        } 
 
        if (!bOpenGL11) { 
            if (bSmoothShading) { 
                glNormal3fv((GLfloat *)&pn3v3[0].normal); 
                glVertex3fv((GLfloat *)&pn3v3[0].vertex); 
                glNormal3fv((GLfloat *)&pn3v3[1].normal); 
                glVertex3fv((GLfloat *)&pn3v3[1].vertex); 
                glNormal3fv((GLfloat *)&pn3v3[2].normal); 
                glVertex3fv((GLfloat *)&pn3v3[2].vertex); 
                glNormal3fv((GLfloat *)&pn3v3[3].normal); 
                glVertex3fv((GLfloat *)&pn3v3[3].vertex); 
            } else { 
                glNormal3fv((GLfloat *)&pn3v3[0].normal); 
                glVertex3fv((GLfloat *)&pn3v3[0].vertex); 
                glVertex3fv((GLfloat *)&pn3v3[1].vertex); 
                glVertex3fv((GLfloat *)&pn3v3[2].vertex); 
                glVertex3fv((GLfloat *)&pn3v3[3].vertex); 
            } 
        } 
    } 
 
    if (bOpenGL11) { 
        glDrawArrays(GL_QUADS, 0, explodeMesh.numFaces * 4); 
    } else { 
        glEnd(); 
    } 
 
    if (restCount) { 
        restCount--; 
        goto resting; 
    } 
 
    if (direction) { 
        maxR = r; 
        r += (float) (0.3 * pow((double)(STEPS - count) / (double)STEPS, 4.0)); 
    } else { 
        r -= (float) (maxR / (double)(STEPS)); 
    } 
 
    count++; 
    if (count > STEPS) { 
        direction ^= 1; 
        count = 0; 
 
        if (direction == 1) { 
            restCount = 10; 
            r = 0.0f; 
 
            for (i = 0; i < explodeMesh.numFaces; i++) { 
                ss_matrixIdent(&faceMat[i]); 
                xstep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
                ystep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
                zstep[i] = (float) (((float)(rand() & 0x3) * PI) / ((float)STEPS + 1.0)); 
                 
                xrot[i] = 0.0f; 
                yrot[i] = 0.0f; 
                zrot[i] = 0.0f; 
            } 
        } 
    } 
 
resting: 
    ; 
}