/*****************************************************************
  tex3d.cpp
  by Dave Pape
  15 April 2003

  This program demonstrates the creation of a 3D texture, and
  applies it to the teapot using texgen for all 3 coordinates
  (S, T, & R).

*****************************************************************/
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <dms/dms.h>

using namespace dms;

void createTexture(void);
void drawEverything(void);

void key(unsigned char k, int x, int y);
void specialkey(int k, int x, int y);
void idle(void);


PerspCamera camera;
Light light;
Material *material;

GLuint textureID, textureID_2D;

bool texture3D=false;


int main(int argc, char *argv[])
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(512,512);
    glutCreateWindow(argv[0]);
	material = new Material(Color::White);
    
    glutDisplayFunc(drawEverything);
    glutKeyboardFunc(key);
    glutSpecialFunc(specialkey);
    glutIdleFunc(idle);
    
    camera.setPosition(0, 0, 10);
    light.setInfinitePosition(-5, 4, 1);
    createTexture();
    
    glutMainLoop();
    return 0;
    }



/*************** CODE OF INTEREST ******************************/
/* The texture image is now a 3D array of colors.              */
/*                                                             */
#define TEX_HEIGHT 16
#define TEX_WIDTH 16
#define TEX_DEPTH 16

struct _color { GLubyte r,g,b; };
struct _color textureImage[TEX_DEPTH][TEX_HEIGHT][TEX_WIDTH];


void createTexture(void)
    {
/*************** CODE OF INTEREST ******************************/
/* Fill in the 3D image array with red & white pixels that will*/
/* produce a brick-like pattern.                               */
/*                                                             */
    int i,j,k;
    for (i = 0; i < TEX_WIDTH; i++)
        for (j = 0; j < TEX_HEIGHT; j++)
            for (k = 0; k < TEX_DEPTH; k++)
                {
                if ((j % 8 == 7) || (k % 8 == 7) ||
                    ((j%16 < 8) && (i%8 == 7)) ||
                    ((j%16 >= 8) && ((i+4)%8 == 7)))
                    {
                    textureImage[k][j][i].r = 255;
                    textureImage[k][j][i].g = 255;
                    textureImage[k][j][i].b = 255;
                    }
                else
                    {
                    textureImage[k][j][i].r = 255;
                    textureImage[k][j][i].g = 0;
                    textureImage[k][j][i].b = 0;
                    }
                }
/*************** CODE OF INTEREST ******************************/
/* Define the 3D texture.  This is similar to defining a 2D    */
/* texture, but using the target GL_TEXTURE_3D and the         */
/* glTexImage3D function.                                      */
/*                                                             */
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_3D, textureID);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT, TEX_DEPTH,
                0, GL_RGB, GL_UNSIGNED_BYTE, textureImage);
    glBindTexture(GL_TEXTURE_3D, 0);

/*************** CODE OF INTEREST ******************************/
/* For comparison, we also create a 2D texture using the same  */
/* data.  This texture will just use the first "plane" of      */
/* image data.                                                 */
/*                                                             */
    glGenTextures(1, &textureID_2D);
    glBindTexture(GL_TEXTURE_2D, textureID_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT,
                0, GL_RGB, GL_UNSIGNED_BYTE, textureImage);
    glBindTexture(GL_TEXTURE_2D, 0);
    }


void drawEverything(void)
    {
    glClearColor(0.5, 0.7, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    camera.apply();
    
    light.apply();
    material->apply();

    glRotatef(currentTime() * 2, 1, 1, 1);

/*************** CODE OF INTEREST ******************************/
/* To apply a 3D texture, we now need 3 coordinates - S, T, & R*/
/* These will all be generated using texgen in this case.      */
/* A program can also explicitly set the coordinates using     */
/* the function glTexCoord3f().                                */
/*                                                             */
    if (texture3D)
        {
        glEnable(GL_TEXTURE_3D);
        glBindTexture(GL_TEXTURE_3D, textureID);
        glEnable(GL_TEXTURE_GEN_S);
        glEnable(GL_TEXTURE_GEN_T);
        glEnable(GL_TEXTURE_GEN_R);
        GLfloat SplaneCoefficients[4] = { 1, 0, 0, 0 };
        GLfloat TplaneCoefficients[4] = { 0, 1, 0, 0 };
        GLfloat RplaneCoefficients[4] = { 0, 0, 1, 0 };
        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
        glTexGenfv(GL_S, GL_EYE_PLANE, SplaneCoefficients);
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
        glTexGenfv(GL_T, GL_EYE_PLANE, TplaneCoefficients);
        glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
        glTexGenfv(GL_R, GL_EYE_PLANE, RplaneCoefficients);
        }
    else
        {
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, textureID_2D);
        }
    
    glutSolidTeapot(2.0);
    
    glDisable(GL_TEXTURE_3D);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_R);

    glutSwapBuffers();

    checkGLError("end-of-frame");
    }


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        exit(0);
    else if (k == ' ')
        texture3D = !texture3D;
    }


void specialkey(int k, int x, int y)
    {
    if (k == GLUT_KEY_LEFT)
        camera.turn(3);
    else if (k == GLUT_KEY_RIGHT)
        camera.turn(-3);
    else if (k == GLUT_KEY_UP)
        camera.pitch(2);
    else if (k == GLUT_KEY_DOWN)
        camera.pitch(-2);
    else if (k == GLUT_KEY_HOME)
        camera.moveForward(0.25);
    else if (k == GLUT_KEY_END)
        camera.moveForward(-0.25);
    else if (k == GLUT_KEY_PAGE_UP)
        camera.zoom(-1);
    else if (k == GLUT_KEY_PAGE_DOWN)
        camera.zoom(1);
    }


void idle(void)
    {
    glutPostRedisplay();
    }

