/*
    licht1a.c

    - erster Test der OpenGL-Beleuchtung (3D-Szene ähnlich raum1a.c)

    - Leertaste:    Animation ein/aus
    - Taste 1:      Wireframe/Polygon umschalten
    - Taste 2:      Tiefenpuffer-Test ein/aus
    - Taste 3:      Weisse Lichtquelle ein/aus
    - Taste 4:      Farbige Lichtquellen ein/aus
    - Taste q:      Programm-Abbruch


    > Alle verwendete GL-Lichtquellen simulieren parallel einfallendes
      Licht. Die Richtung, aus der das Licht jeweils kommt, wird mit
      dem GL_POSITION-Parameter in glLightfv(...) gesetzt.
      Welche Werte muss man wählen, damit z.B. die weisse Lichtquelle
      senkrecht von oben scheint? Oder senkrecht von unten?
      (Hinweis: Das Koordinatensystem ist das gleiche wie in raum1a.c.)

    > Die Materialeigenschaften (Farbe, Reflektivität) aller Objekte sind
      gleich: Es werden die Voreinstellungen von GL benutzt, die eine
      weiße, diffus reflektierende Oberfläche modellieren. (Zum Einstellen
      anderer Eigenschaften gibt es glMaterial(...) -- das kommt in licht2a.c.)
      Insbesondere werden die Aufrufe von glColor3f(...) ignoriert,
      sobald die GL-Beleuchtung eingeschaltet ist. Dies sieht man
      (hoffentlich), wenn man die weisse Lichtquelle ein- und ausschaltet
      während die farbigen Lichtquellen ganz ausgeschaltet bleiben.

    > Farbe kommt in diesem Beispiel also nur durch die Lichtquellen ins
      Spiel. Wenn alle Lichtquellen eingeschaltet sind, erscheinen die
      Objekte zu "hell" oder das Bild "überbelichtet". Da es im Gegensatz
      zum Fotoapparat in GL keine Blende oder Belichtungszeit gibt,
      bleibt nur die Lösung, die Intensität der Lichtquellen abzuschwächen.
      Mit welchen Werten für glLightfv (GL_LIGHTx, GL_DIFFUSE, ...);
      ergibt sich eine "ausgewogene" Lichtsituation?
      Man kann in GL übrigens bis zu acht Lichtquellen gleichzeitig
      nutzen.
*/



# include       <stdio.h>
# include       <stdlib.h>
# include       <math.h>
# include       <GL/glut.h>


# define        MILLISEC_PRO_FRAME      50



static GLuint   objekt_raum;
static int      animation_laeuft = 0,
                polygone_als_linien = 0,
                tiefenpuffer_test = 1,
                weisse_lichtquelle = 0,
                farbige_quellen = 0,
                fenster_breite,
                fenster_hoehe;
static float    theta = 0.5 * M_PI;  /* der aktuelle Rotationswinkel */



static void     timer_func (int value)
/************************************/
{
    /* printf ("timer_func (value=%d)\n", value); */

    theta += 0.005;
    glutPostRedisplay ();
}



static void     erzeuge_objekt_liste (void)
/*****************************************/
{
    int         i,
                n_lines = 20,
                n_quads = 10;
    float       phi,
                x, z;


    objekt_raum = glGenLists (1);
    glNewList (objekt_raum, GL_COMPILE);

    /* Icosaeder */
    glPushMatrix ();
        glTranslatef (1.5, 1, 1.5);
        glColor3f (0.3, 0.3, 0.3);
        glutSolidIcosahedron ();
    glPopMatrix ();

    /* Kugel */
    glPushMatrix ();
        glTranslatef (-1.5, 1, 1.5);
        glColor3f (0.3, 0.3, 0.3);
        glutSolidSphere (0.95, 10, 10);
    glPopMatrix ();

    /* Rundkegel */
    glPushMatrix ();
        glTranslatef (1.5, 0, -1.5);
        glRotatef (-90, 1, 0, 0);
        glutSolidCone (0.75, 4, 10, 3);
    glPopMatrix ();

    /* Quader-Säule */
    glPushMatrix ();
        glTranslatef (-1.5, 2, -1.5);
        glScalef (1, 4, 1);
        glColor3f (0.3, 0.3, 0.3);
        glutSolidCube (1);
    glPopMatrix ();

    /* kleiner "Teppich" */
    glPushMatrix ();
        glTranslatef (0, -0.01, 0);
        glColor3f (0.8, 0.8, 0.8);
        glNormal3f (0, 1, 0);
        glBegin (GL_QUADS);
            glVertex3f (-1, 0, -1);
            glVertex3f (+1, 0, -1);
            glVertex3f (+1, 0, +1);
            glVertex3f (-1, 0, +1);
        glEnd ();
    glPopMatrix ();

    /* Boden-Ebene */
    glPushMatrix ();
        glTranslatef (0, -0.02, 0);
        glColor3f (1, 1, 1);
        glNormal3f (0, 1, 0);
        glBegin (GL_QUADS);
            glVertex3f (-5, 0, -5);
            glVertex3f (+5, 0, -5);
            glVertex3f (+5, 0, +5);
            glVertex3f (-5, 0, +5);
        glEnd ();
    glPopMatrix ();

    glEndList ();  /* Ende objekt_raum */
}



static void     display_func (void)
/*********************************/
{
    float       x, z;

    if (animation_laeuft)
        glutTimerFunc (MILLISEC_PRO_FRAME, timer_func, 1);

    /* printf ("display_func()\n"); */

    glClearColor (0.75, 0.75, 0.75, 0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* Kamera wie in raum1.c: */

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, fenster_breite / (float) fenster_hoehe, 1, 30);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    x = 8 * cos (theta);
    z = 8 * sin (theta);
    /*         Auge:      Target:    "oben":   */
    gluLookAt (x, 5, z,   0, 1, 0,   0, 1, 0);


    /* Die aktuelle Einstellung bestimmer Attributgruppen sichern: */
    glPushAttrib (GL_ENABLE_BIT | GL_POLYGON_BIT);

    if (polygone_als_linien)
        glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);

    if (tiefenpuffer_test)
        glEnable (GL_DEPTH_TEST);

    if (weisse_lichtquelle)
    {
        GLfloat     weiss[4]      = { 1,   1,   1,   1 },
                    grau[4]       = { 0.5, 0.5, 0.5, 1 },
                    schwarz[4]    = { 0,   0,   0,   1 },
                    /* homogene Koordinaten: letzte 0 -> "Richtungsvektor": */
                    vorne_oben[4] = { 0,   0.5, 1,   0 };

        /* Die Licht-"Farbe": */
        glLightfv (GL_LIGHT0, GL_DIFFUSE,  weiss);

        /* Diese Parameter sollen hier einfach unterdrückt werden: */
        glLightfv (GL_LIGHT0, GL_SPECULAR, schwarz);
        glLightfv (GL_LIGHT0, GL_AMBIENT,  schwarz);

        /* Die Richtung, aus der das Licht parallel einfällt: */
        glLightfv (GL_LIGHT0, GL_POSITION, vorne_oben);

        /* Jede Lichtquelle muss einzeln eingeschaltet werden: */
        glEnable  (GL_LIGHT0);
    }

    if (farbige_quellen)
    {
        GLfloat     gelblich[4]    = { 1.0, 1.0, 0.6, 1 },
                    blaeulich[4]   = { 0.6, 0.6, 1.0, 1 },
                    grau[4]        = { 0.5, 0.5, 0.5, 1 },
                    rechts_oben[4] = {  1, 1, 0, 0 },
                    links_oben[4]  = { -1, 1, 0, 0 };

        /* Analog zur weissen Lichtquelle; nur brauchen */
        /* wir hier keine Parameter zu unterdrücken, da sie */
        /* per Voreinstellung schon inaktiv sind. */

        glLightfv (GL_LIGHT1, GL_DIFFUSE,  gelblich);
        glLightfv (GL_LIGHT1, GL_POSITION, links_oben);
        glEnable  (GL_LIGHT1);

        glLightfv (GL_LIGHT2, GL_DIFFUSE,  blaeulich);
        glLightfv (GL_LIGHT2, GL_POSITION, rechts_oben);
        glEnable  (GL_LIGHT2);
    }

    if (weisse_lichtquelle || farbige_quellen)
    {
        /* Ausser den einzelnen Lichtquelle muss die */
        /* Beleuchtung "an sich" eingeschaltet werden: */
        glEnable (GL_LIGHTING);

        /* Hierdurch werden als Flächennormalen automatisch
        /* auf die erforderliche Einheitslänge normiert: */
        glEnable (GL_NORMALIZE);
    }

    glCallList (objekt_raum);

    /* Die ursprüngliche Einstellung der gesicherten Attributgruppen */
    /* wiederherstellen: */
    glPopAttrib ();

    glutSwapBuffers ();
    glutReportErrors ();
}



static void     reshape_func (int width, int height)
/**************************************************/
{
    /* printf ("reshape_func (width=%d, height=%d)\n", width, height); */

    glViewport (0, 0, width, height);

    fenster_breite = width;
    fenster_hoehe = height;
}



static void     keyboard_func (unsigned char key, int x, int y)
/*************************************************************/
{
    switch (key)
    {
        case ' ':   /* Leertaste */
            if (animation_laeuft)
                animation_laeuft = 0;
            else
                animation_laeuft = 1;
            printf ("Animation %s\n", animation_laeuft ? "ein" : "aus");
            glutPostRedisplay ();
            break;

        case '1':
            polygone_als_linien = ! polygone_als_linien;
            printf ("Darstellung als %s\n",
                polygone_als_linien ? "Wireframe" : "Flächen");
            glutPostRedisplay ();
            break;

        case '2':
            tiefenpuffer_test = ! tiefenpuffer_test;
            printf ("Tiefenpuffer-Test (verdeckte Flächen) %s\n",
                tiefenpuffer_test ? "ein" : "aus");
            glutPostRedisplay ();
            break;

        case '3':
            weisse_lichtquelle = ! weisse_lichtquelle;
            printf ("Weisse Lichtquelle %s\n",
                weisse_lichtquelle ? "ein" : "aus");
            glutPostRedisplay ();
            break;

        case '4':
            farbige_quellen = ! farbige_quellen;
            printf ("Farbige Lichtquellen %s\n",
                farbige_quellen ? "ein" : "aus");
            glutPostRedisplay ();
            break;

        case 'q':
        case 'Q':
            exit (0);
    }
}



extern int      main (int argc, char **argv)
/******************************************/
{
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

    glutInitWindowPosition (200, 100);
    glutInitWindowSize (600, 600);
    glutCreateWindow ("licht1");

    glutReshapeFunc (reshape_func);
    glutDisplayFunc (display_func);
    glutKeyboardFunc (keyboard_func);

    erzeuge_objekt_liste ();

    glutMainLoop ();

    return 0;
}


