
/*
 * this is a translation of a pascal program from
 *   http://home.kabelfoon.nl/~rhordijk/progs.html
 * which was, in turn, inspired by the work of william latham
 *
 * this code should live at http://www.nixfiles.com/src/horn/
 */

#include <windows.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#define PI 3.1415926536

//#define TRIANGLES
#define SPHERES
//#define BIGSPHERES
//#define POINTS
//#define LIGHTMAP

#ifdef TRIANGLES
# define  NRPOINTS  80
# define  NRLINES   30
#endif
#ifdef SPHERES
# define  NRPOINTS  80
# define  NRLINES   30
#endif
#ifdef BIGSPHERES
# define SPHERES
# define  NRPOINTS  20
# define  NRLINES   5
#endif
#ifdef POINTS
# define  NRPOINTS  250
# define  NRLINES   30
#endif
#ifdef LIGHTMAP
# define  NRPOINTS  150
# define  NRLINES   20
#endif

int screenwidth;
int screenheight;

#ifdef SPHERES
GLUquadricObj *shapes;
#endif  

DWORD t0;


void
horn(int ribs, double bend, double stack, double twist,
     double twisttrans, double grow)
{
    int i;
    for (i = 0; i < ribs; i++) {
        double t = ((double) i) / ribs;
        glPushMatrix();
        glRotated(bend * t, 1, 0, 0);
        glTranslated(0, 0, stack * t);
        glTranslated(twisttrans, 0, 0);
        glRotated(twist * t, 0, 0, 1);
        glTranslated(-twisttrans, 0, 0);

#ifdef TRIANGLES
        glBegin(GL_TRIANGLES);
        glColor4d(0.9,0.8,0.5, 1);
        glVertex3d(2,2,0);
        glColor4d(0.9,0.8,0.5,0.0);
        glVertex3d(0,2,0);
        glVertex3d(2,0,0);
        glEnd();
#endif
#ifdef BIGSPHERES
        gluQuadricDrawStyle(shapes, GLU_FILL);
        gluQuadricNormals(shapes, GLU_SMOOTH);
        gluSphere(shapes, 7 * exp(i * log(grow) / ribs), 20, 10);
#else
#ifdef SPHERES
        gluQuadricDrawStyle(shapes, GLU_FILL);
        gluQuadricNormals(shapes, GLU_SMOOTH);
        // gluSphere(shapes, pow(grow, (1.0 * i) / ribs), 5, 5);
        gluSphere(shapes, exp(i * log(grow) / ribs), 6, 4);
#endif
#endif
#ifdef LIGHTMAP
        glBegin(GL_POINTS);
        glVertex3d(0,0,0);
        glEnd();
#endif
#ifdef POINTS
        glBegin(GL_POINTS);
        glVertex3d(0,0,0);
        glEnd();
#endif
        glPopMatrix();
    }
}

void
ResizeGL(int width, int height)
{
    screenwidth = width;
    screenheight = height;
    glViewport(0 , 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30, ((double) width) / height, 100, 300);
}

void
horn_initgl()
{
    static GLfloat glfLightPosition[] = { 100.0, 100.0, 100.0, 0.0 };
    static GLfloat glfFog[] = { 0.0, 0.0, 0.3, 1.0 };
    static GLfloat DiffuseLightColor[] = { 1, 0.8, 0.4, 1.0 };
#ifdef LIGHTMAP
    int i, j;
    double x, y, r, d;
    byte texbuf[4096];
#endif

    glEnable(GL_DEPTH_TEST);

    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

#ifdef SPHERES
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
#endif

#ifdef SPHERES
    glLightfv(GL_LIGHT0, GL_POSITION, glfLightPosition);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glLightfv(GL_LIGHT0, GL_DIFFUSE , DiffuseLightColor);
#endif

#ifdef POINTS
    glHint (GL_POINT_SMOOTH_HINT, GL_NICEST);
    glPointSize(3);
    glEnable (GL_POINT_SMOOTH);
#endif

#ifdef SPHERES
#if 0
    glFogi (GL_FOG_MODE, GL_LINEAR);
    glHint (GL_FOG_HINT, GL_NICEST);
    glFogfv (GL_FOG_COLOR, glfFog);
    glFogf(GL_FOG_START, 150);
    glFogf(GL_FOG_END, 550);
    glEnable(GL_FOG);
#endif
#endif
#ifdef POINTS
    glFogi (GL_FOG_MODE, GL_LINEAR);
    glHint (GL_FOG_HINT, GL_NICEST);
    glFogfv (GL_FOG_COLOR, glfFog);
    glFogf(GL_FOG_START, 150);
    glFogf(GL_FOG_END, 250);
    glEnable(GL_FOG);
#endif

#ifdef SPHERES
    shapes = gluNewQuadric();
#endif

#ifdef LIGHTMAP
    r = 32;
    for (i = 0; i < 64; i++) {
        for (j = 0; j < 64; j++) {
            x = fabs(i - 32);
            y = fabs(j - 32);
            d = sqrt(x * x + y * y);
            if (d < r) {
                d = 1 - (d / r);
                texbuf[i * 64 + j] = (byte) 255 * d * d;
            } else {
                texbuf[i * 64 + j] = 0;
            }
        }
    }

    gluBuild2DMipmaps(GL_TEXTURE_2D, 1, 64, 64,
                      GL_LUMINANCE,
                      GL_UNSIGNED_BYTE, texbuf);

    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_MIN_FILTER,
                    GL_LINEAR_MIPMAP_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_MAG_FILTER,
                    GL_LINEAR);
#endif

#if 0
#ifdef LIGHTMAP
    // trick to fullscreen
    ShowWindow(wnd,SW_MAXIMIZE);
    PostMessage(wnd,WM_SIZE,0,0);
#endif
#endif
}

#ifdef LIGHTMAP
void
horn_paintgl()
{
    double dt;
    int i,j, offset;
    GLfloat fb_buffer[NRPOINTS * NRLINES * 4];
    double x,y,w;

    dt = (GetTickCount() - t0) * 0.001;

    glDisable(GL_DEPTH_TEST);
    glFeedbackBuffer(NRPOINTS * NRLINES * 4, GL_3D, fb_buffer);
    glRenderMode(GL_FEEDBACK);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    gluLookAt( 200, 0, 0,
               0, 0, 0,
               0, 0, 1);

    glPushMatrix();
    glRotated(90, 0, 1, 0);
    glRotated(10.0 * dt, 0, 0, 1);

    for (i = 0; i < NRLINES; i++) {
        glPushMatrix();
        glRotated((360.0 * i) / NRLINES, 0, 0, 1);
        glRotated(45, 1, 0, 0);
        glTranslated(0, 0, 2);
        horn(NRPOINTS,
             315 * sin(dt * 0.237),
             45 + 20 * sin(dt * 0.133),
             1500 * sin(dt * 0.213),
             7.5 + 2.5 * sin(dt * 0.173),
             1);

        glPopMatrix();
    }
    glPopMatrix();
    glPopMatrix();

    glRenderMode(GL_RENDER);

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glScalef(2.0 / screenwidth, 2.0 / screenheight, 1.0);
    glTranslatef(0.5 * -screenwidth, 0.5 * -screenheight, 0.0);

    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_TEXTURE_2D);
    glBlendFunc(GL_ONE, GL_ONE);
    glEnable(GL_BLEND);

    glBegin(GL_QUADS);

    for (j = 0; j < NRPOINTS; j++) {
        glColor3f((0.25 - 0.15  * j) / NRPOINTS,
                  (0.15 + 0.10 * j) / NRPOINTS,
                  (0.06 + 0.14 * j) / NRPOINTS);
        for (i = 0; i < NRLINES; i++) {
            offset = 4 * (i * NRPOINTS + j);
            x = fb_buffer[offset + 1];
            y = fb_buffer[offset + 2];

            w = 16;

            glTexCoord2f(0.0, 0.0);
            glVertex2f(x - w, y - w);

            glTexCoord2f(1.0, 0.0);
            glVertex2f(x + w, y - w);

            glTexCoord2f(1.0, 1.0);
            glVertex2f(x + w, y + w);

            glTexCoord2f(0.0, 1.0);
            glVertex2f(x - w, y + w);
        }
    }

    glEnd();

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

}

#else

void
horn_paintgl()
{
    double dt;
    int i;

    // dt = (GetTickCount() - t0) * 0.0000001;
    dt = (GetTickCount() - t0) * 1e-4;

    glClearColor(0, 0, 0.3, 0);

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30, ((double) screenwidth) / screenheight, 100, 300);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 200, 00, 0,
               0, 0, 0,
               0, 0, 1);

    glPushMatrix();

//    glRotated(90, 0, 1, 0);
    glRotated(10 * dt, 0, 1, 0);

    glRotated(10 * dt, 0, 0, 1);

    glColor3d(0.9,0.7,0.5);

    glNewList(1, GL_COMPILE);
    horn(NRPOINTS,
         315 * sin(dt * 0.237),
         50 + 20 * sin(dt * 0.133),
         1500 * sin(dt * 0.213),
         7.5 + 2.5 * sin(dt * 0.173),
         0.6 + 0.1 * sin(dt * 0.317));
    glEndList();

    for (i = 0; i < NRLINES; i++) {
        glPushMatrix();
        glRotated((360.0 * (double) i) / NRLINES, 0, 0, 1);
        glRotated(45, 1, 0, 0);
        glTranslated(0, 0, 2);
        glCallList(1);

        glPopMatrix();
    }

    glPopMatrix();
}
#endif

void
CloseGL()
{
#ifdef SPHERES
    gluDeleteQuadric(shapes);
#endif
}
