/*
 *
 * File: ac3d_view.c
 *
 * This code-example was written by Andreas Paul in June 1997 for the course
 * `"Ubung zur Vorlesung 'Graphische Datenverarbeitung'` held at the Technische
 * Universit"at M"unchen (http://www5.in.tum.de/~gdv/).
 *
 * Copyright (C) 1997 Andreas Paul
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * The GPL can be found at http://www.gnu.org/ , the last time I checked the
 * URL was http://www.gnu.org/copyleft/gpl.html
 *
 *--
 * E-Mail: Andreas.Paul@in.tum.de        | Roger Waters (Amused to Death):
 * IRC   : Co                            |  It all makes perfect sense
 * URL   : http://www5.in.tum.de/~paula/ |  Expressed in dollars and cents
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#define WINDOW_WIDTH    768
#define WINDOW_HEIGHT   576

GLuint  display_list;

void set_modelview()
{
    GLfloat light_position[] = { 100.0, 100.0, 100.0, 0.0 };

    static double phi   = 0.0;
    static double theta = 0.0;
    static double psi   = 30.0;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glTranslated(0.0, -0.1, -4.0);

    glRotated(psi  , 1.0, 0.0, 0.0); /* X-Achse */
    glRotated(theta, 0.0, 1.0, 0.0); /* Y-Achse */
    glRotated(phi  , 0.0, 0.0, 1.0); /* Z-Achse */

    psi   += 0.2; /* X-Achse */
    theta += 0.9; /* Y-Achse */
    phi   += 0.4; /* Z-Achse */
}

void set_projection(int w, int h)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (float)w/(float)h, 0.1, 10.0);
}

void init_gfx(void)
{
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

    glShadeModel(GL_FLAT);
    glClearColor(0,0,0,0);

    set_projection(WINDOW_WIDTH,WINDOW_HEIGHT);
}

void drawscene(void)
{
    set_modelview();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glCallList(display_list);

    glFlush();
    glFinish();
    glutSwapBuffers();
}

void reshape_gfx(int w, int h)
{
    glViewport(0, 0, w, h);
    set_projection(w,h);
}

void keyboard_input(unsigned char key, int x, int y)
{
    switch(key) {
        case  27: exit(0); break;
        default: printf(" Key pressed: >%c< (%d) at position x=%d y=%d\n",key,key,x,y); break;
        }
}

void normal(float *v1, float *v2, float *v3, float *n)
{
    double  ax,ay,az;
    double  bx,by,bz;
    double  length;
    ax = v1[0] - v2[0];
    ay = v1[1] - v2[1];
    az = v1[2] - v2[2];
    bx = v3[0] - v2[0];
    by = v3[1] - v2[1];
    bz = v3[2] - v2[2];
    n[0] = +( ay*bz - az*by );
    n[1] = -( ax*bz - az*bx );
    n[2] = +( ax*by - ay*bx );
    length=sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
    n[0] /= length;
    n[1] /= length;
    n[2] /= length;
}

GLuint read_ac3d_triangle_file(char *fname)
{
    FILE    *in;
    char    line[128];
    GLfloat v1[3],v2[3],v3[3],n[3];
    GLuint  col,last_col;
    GLuint  list;

    last_col=-1;

    list = glGenLists(1);

    in=fopen(fname,"r");

    if(in==NULL) {
        fprintf(stderr,"ERROR: opening file %s\n",fname);
        exit(1);
        }

    fprintf(stderr," reading data ... ");

    glNewList(list,GL_COMPILE);

    glBegin(GL_TRIANGLES);

    while(fgets(line,128,in)!=NULL) {
        sscanf(line,"%f %f %f  %f %f %f  %f %f %f  0x%X",&v1[0],&v1[1],&v1[2],
                                                         &v2[0],&v2[1],&v2[2],
                                                         &v3[0],&v3[1],&v3[2],
                                                         &col);
        if(col!=last_col) {
            col = col<<8;
            glColor3ubv((GLubyte *)&col);
            last_col=col;
            }
        normal(v3,v2,v1,n);
        glNormal3fv(n);
        glVertex3fv(v1);
        glVertex3fv(v2);
        glVertex3fv(v3);
        }

    glEnd();
    glEndList();

    fprintf(stderr,"done\n");

    fclose(in);

    return(list);
}

void usage(char *prg_name)
{
    printf("Usage: %s <filename>\n",prg_name);
    printf("             <filename> has to be a ac3d triangle list file.\n");
    exit(1);
}

void main(int argc, char **argv)
{
    if(argc <= 1) usage(argv[0]);

    glutInit(&argc, argv);

    glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH );
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);

    glutCreateWindow(argv[0]);

    glutReshapeFunc(reshape_gfx);
    glutDisplayFunc(drawscene);
    glutIdleFunc(drawscene);
    glutKeyboardFunc(keyboard_input);

    init_gfx();

    display_list = read_ac3d_triangle_file(argv[1]);

    glutMainLoop();
}
