/*
    texture3.c

    - Texture-Mapping, 2 Texturen werden aus Tiff-Dateien gelesen
      und in Textur-Objekten gespeichert.
      Dies erfordert mindestens OpenGL 1.1 -- dies wird auch abgefragt.

    - Aufruf:  texture3 Tiff-Datei1 Tiff-Datei2
      Dabei müssen Breite und Höhe der Tiff-Dateien
      Zweier-Potenzen sein, also z.B. 128 oder 256.
      Tip: Für den GL-Kurs gibt es zwei Dateien test.tif und test2.tif.
    - !!! Achtung, beim Kompilieren und Linken wird die libtiff-Library
      benötigt, darum bitte das aktuelle GNUmakefile benutzen!
    - Programm-Ende durch Drücken einer beliebigen Taste.


    > In diesem Programm werden zwei Texturen aus zwei Tiff-Dateien
      gelesen und in GL geladen. Die Funktion tiff_bild_lesen()
      ist unverändert. Die Textur-Befehle für GL stehen jetzt
      in einer neuen Funktion textur_laden(). Diese wird aufgerufen,
      NACHDEM das Glut-Fenster erzeugt wurde, und BEVOR die
      display_func() durch Glut aufgerufen wird. Somit geschieht
      der gesamte Ladevorgang pro Textur nur ein einziges Mal,
      nämlich am Programm-Anfang. -> Siehe main().

    > Damit GL die beiden Texturen unterscheiden kann,
      wird mit glBindTexture(...) jeweils eine "aktiviert".
      Laden, Parameter einstellen und rendern beziehen sich
      immer auf die zuletzt aktivierte Textur. GL vergibt
      für die Texturen interne Nummern -- dies geschieht mit
      glGenTextures(...). In anderen System (nicht OpenGL)
      werden solche internen Nummern auch als "Handles" bezeichnet.
      Das Programm speichert diese Nummern oder Handles, die die
      Texturen (oder "Textur-Objekte") repräsentieren, in dem Array
      textur_objekte[]. -> Siehe Aufrufe und Kommentare in
      textur_laden() und display_func().

*/



# include       <stdio.h>
# include       <stdlib.h>
# include       <math.h>
# include       <tiffio.h>      /* Sam Leffler's libtiff library. */
# include       <GL/glut.h>


# if !defined (GL_VERSION_1_1)
# error "Dieses Programm benötigt OpenGL 1.1!"
# else


# define        FEHLER_ENDE(text, wert) \
                {  \
                    fprintf (stderr, text, wert);  \
                    fprintf (stderr, " (in %s, %d)\n", __FILE__, __LINE__);  \
                    exit (1);  \
                }


static GLuint   textur_objekte[2];



static void     display_func (void)
/*********************************/
{
    int         i,
                n = 8,
                h, b;
    float       x, y,
                phi,
                d;

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

    glLoadIdentity ();
    gluOrtho2D (-2, 2, -2, 2);

    glClearColor (1, 1, 1, 0);
    glClear (GL_COLOR_BUFFER_BIT);

    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    glPointSize (10);

    for (i = 0; i < n; i ++)
    {
        phi = 2 * M_PI * i / (float) n;
        x = cos (phi);
        y = sin (phi);
        d =  (i + 1) / (float) n;

        /* Die beiden Texturen immer abwechseln benutzen: */
        glEnable (GL_TEXTURE_2D);
        glBindTexture (GL_TEXTURE_2D, textur_objekte[i % 2]);

        glBegin (GL_QUADS);
            glTexCoord2f (    0,     0);
              glVertex2f (    x,     y);

            glTexCoord2f (    1,     0);
              glVertex2f (x + d,     y);

            glTexCoord2f (    1,     1);
              glVertex2f (x + d, y + d);

            glTexCoord2f (    0,     1);
              glVertex2f (    x, y + d);
        glEnd ();
        glDisable (GL_TEXTURE_2D);

        glColor3f (0, 0, 0);
        glBegin (GL_POINTS);
            glVertex2f (x, y);
        glEnd ();
    }

    glFlush ();
    glutReportErrors ();
}



static void     keyboard_func (unsigned char key, int x, int y)
/*************************************************************/
{
    /* printf ("keyboard_func (key='%c', x=%d, y=%d)\n", key, x, y); */

    exit (0);
}



static void     tiff_bild_lesen (const char *dateiname,
                    GLubyte **gl_pixels, int *breite, int *hoehe)
/***************************************************************/
{
    char        fehlertext[1024];
    TIFF        *tiff_datei;
    TIFFRGBAImage
                tiff_bild;
    uint32      *tiff_pixels;
    int         n_pixels,
                i;


    /* Tiff-Datei öffnen und einlesen: */

    tiff_datei = TIFFOpen (dateiname, "r");
    if (tiff_datei == NULL)
        FEHLER_ENDE ("Datei '%s' kann nicht geöffnet werden", dateiname);

    if (TIFFRGBAImageBegin (&tiff_bild, tiff_datei, 0, fehlertext) == 0)
        FEHLER_ENDE ("Fehler beim Lesen der Tiff-Datei (%s)", fehlertext);

    n_pixels = tiff_bild.width * tiff_bild.height;
    *breite = tiff_bild.width;
    *hoehe = tiff_bild.height;

    tiff_pixels = _TIFFmalloc (n_pixels * sizeof (uint32));
    if (tiff_pixels == NULL)
        FEHLER_ENDE ("Fehler beim Anlegen der Tiff-Pixel", 0);

    if (TIFFRGBAImageGet (&tiff_bild, tiff_pixels, *breite, *hoehe) ==0)
        FEHLER_ENDE ("Fehler beim Lesen der Tiff-Datei", 0);

    TIFFRGBAImageEnd (&tiff_bild);


    /* Das Tiff-Bild steht jetzt in tiff_pixels (uint32).
       Es muss für OpenGl in GL_RGBA-Pixels (GLubyte[3]) umgewandelt werden:
     */

    *gl_pixels = (GLubyte *) malloc (n_pixels * 3 * sizeof (GLubyte));
    if (gl_pixels == 0)
        FEHLER_ENDE ("Fehler beim Anlegen der GL-Pixel", 0);

    for (i = 0; i < n_pixels; i ++)
    {
        (*gl_pixels)[3 * i + 0] =  tiff_pixels[i]        & 0xff;
        (*gl_pixels)[3 * i + 1] = (tiff_pixels[i] >>  8) & 0xff;
        (*gl_pixels)[3 * i + 2] = (tiff_pixels[i] >> 16) & 0xff;
    }

    _TIFFfree (tiff_pixels);    /* Tiff-Bild wird nicht mehr gebraucht */
}



static void     textur_laden (const char *dateiname, GLuint *tex_objekt)
/**********************************************************************/
{
    GLubyte     *textur_pixels;
    int         textur_breite,
                textur_hoehe;

    tiff_bild_lesen (dateiname, &textur_pixels, &textur_breite, &textur_hoehe);

    /* Textur-Objekt erzeugen: */
    glGenTextures (1, tex_objekt);

    /* Textur-Objekt aktivieren: */
    glBindTexture (GL_TEXTURE_2D, *tex_objekt);

    /* Textur in GL laden, wie zuvor in texture2.c: */
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB,
        textur_breite, textur_hoehe, 0,
        GL_RGB, GL_UNSIGNED_BYTE, textur_pixels);

    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    /* Textur-Objekt de-aktivieren: */
    glBindTexture (GL_TEXTURE_2D, 0);

    /* Nach dem die Texture in GL geladen ist, */
    /* brauchen wir die Pixels nicht mehr im Hauptspeicher: */
    free (textur_pixels);
}



extern int      main (int argc, char **argv)
/******************************************/
{
    char        *programname,
                *dateiname1,
                *dateiname2;

    glutInit (&argc, argv);

    programname = argv[0];

    if (argc != 3)
        FEHLER_ENDE ("usage: %s tiff-datei1 tiff-datei2", programname);

    dateiname1 = argv[1];
    dateiname2 = argv[2];

    /* Glut-Fenster, wie gehabt */

    glutInitDisplayMode (GLUT_RGB);

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

    glutDisplayFunc (display_func);
    glutKeyboardFunc (keyboard_func);

    /* Texturen nur EINMAL lesen und laden */
    /* (muss NACH glutCreateWindow(...) geschehen): */
    textur_laden (dateiname1, &textur_objekte[0]);
    textur_laden (dateiname2, &textur_objekte[1]);

    glutMainLoop ();

    return 0;
}

# endif /* GL_VERSION_1_1 */
