package classwork;

import java.nio.*;
import javax.media.opengl.*;
import jocode.*;
import jomodel.*;

/**
 * GLART_6_alphaZsort.java
 *
 * Render the Teapot model with alpha blending.  See render() for
 * special handling for drawing the model with a translucent effect.
 *
 * This app calls JOMesh.projectVerts() to "project" the vertices of
 * the mesh as if they were being drawn to the screen.  Nothing is actually
 * drawn, but the positions of the vertices are recorded in the
 * mesh (GL_Vertex.posS holds the screen position)
 * so that we know the Z depth of every vertex.
 *
 * Then we call JOMesh.sortTriangles() to sort the triangles by
 * their Z depth value, from back to front.
 *
 * Then renderMesh() draws the sorted triangles from back to front and they
 * appear correctly layered on screen.
 */
public class GLART_6_alphaZsort  extends JOApp {
    private float rotation = 0;
    private JOMesh obj;

    // Material object will hold color values
    JOMaterial clearMtl = new JOMaterial();
    JOMaterial opaqueMtl = new JOMaterial();

    // texture handle (a number that refers to an allocated texture)
    int marbleTextureHandle = 0;

    // Buffers to hold matrices for projectVerts() operation.
    // For memory efficiency, instantiate these once and reuse them.
    // see getModelviewMatrix(), getProjectionMatrix(), getViewport() and JOMesh.projectVerts().
    public static DoubleBuffer modelViewMatrix;
    public static DoubleBuffer projectionMatrix;
    public static IntBuffer    viewport;


    /**
     * Main function just creates and runs the application.
     */
    public static void main(String args[]) {
    	GLART_6_alphaZsort app = new GLART_6_alphaZsort();
    	displayWidth = 800;
    	displayHeight = 600;
        app.run();
    }

    /**
     * Initialize OpenGL
     */
    public void setup() {
        // color of overall scene lighting
        float ambient[]       = { 0f, 0f, 0f, 1f };

        // color of light source
        float lightDiffuse[]  = { 1f, 1f, 1f, 1f }; // direct light
        float lightSpecular[] = { 1f, 1f, 1f, 1f }; // highlight
        float lightAmbient[]  = { .2f, .2f, .2f, 1f }; // scattered light

        // light position: if last value is 0, then this describes light direction.
        // If 1, then light position.
        float lightPosition[] = { -4f, 4f, 6, 1f };

        // Create sphere texture
        marbleTextureHandle = makeTexture("images/marble.jpg");

        JOOBJImporter importOBJ = new JOOBJImporter();
        obj = importOBJ.load("models/teapotT.obj");

        // Get the current projection and viewport matrices
        // for use in projectVerts().  Modelview matrix will change
        // every frame, so we'll get that in render().
		projectionMatrix = getProjectionMatrix();
        viewport = getViewport();
        
        // Select the Projection Matrix (controls perspective)
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();    // Reset The Projection Matrix

        // Define perspective
        glu.gluPerspective(
            45.0f,        // Field Of View
            (float)getWidth() / (float)getHeight(), // aspect ratio
            0.1f,         // near Z clipping plane
            1000.0f);      // far Z clipping plane

        // make sure OpenGL correctly layers objects
        gl.glEnable(GL.GL_DEPTH_TEST);

        // OpenGL won't draw backward facing triangles ("back faces")
        gl.glEnable(GL.GL_CULL_FACE);

        // turn lighting on (does not create a light)
        gl.glEnable(GL.GL_LIGHTING);

        // Create a light
        // diffuse is the color of direct light from this light source
        // specular is the hightlight color
        // ambient is the color of scattered light from this source
        // position is where the light is, or it's direction
        setLight( GL.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition );

        // no overall scene lighting
        setAmbientLight(ambient);

        // make the transparent material
        clearMtl.setDiffuse(  new float[] { 1f, 1f, 1f, .7f });    // white  .5f in the fourth position is ALPHA value
        clearMtl.setAmbient(  new float[] { .7f, .7f, .7f, 1f });     // light gray 
        clearMtl.setSpecular( new float[] { .5f, .5f, .5f, 1f });      // almost white: very reflective
        clearMtl.setShininess(50f);   // 0=no shine,  127=max shine

        // make the opaque material
        opaqueMtl.setDiffuse(new float[] { 1f, .2f, .2f, 1f }); // reddish
        opaqueMtl.setAmbient(new float[] { .8f, .2f, .2f, 1f });   // reddish

        // set the background color
        gl.glClearColor(.5f, .5f, .6f, 1);

        // turn blending on (for transparency)
        gl.glEnable(GL.GL_BLEND);
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

        // Force normals to length 1
        gl.glEnable(GL.GL_NORMALIZE);

        // Draw specular highlghts on top of textures (GL12.GL_SINGLE_COLOR to reset)
        gl.glLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL.GL_SEPARATE_SPECULAR_COLOR );
    }

    /**
     * Render the scene.
     */
    public void draw() {
    	rotation += .3f;

		// Clear screen and depth buffer
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Select The Modelview Matrix (controls model orientation)
        gl.glMatrixMode(GL.GL_MODELVIEW);

        // reset the coordinate system to center of screen
        gl.glLoadIdentity();

        // Where is the 'eye'
        glu.gluLookAt(
            0f, 4f, 8f,   // eye position
            0f, 1.5f, 0f,    // target to look at
            0f, 1f, 0f);   // which way is up

        // rotate scene
        gl.glRotatef(rotation, 0,1,0);

        opaqueMtl.apply();                       // set the opaque material 
        gl.glBindTexture(GL.GL_TEXTURE_2D, 0);   // reset to the default blank texture (0)
        gl.glPushMatrix();
        {
        	gl.glRotatef(-90, 1,0,0);
        	renderPlane(10,10);
        }
        gl.glPopMatrix();
        
        // get the current modelView Matrix (right before we draw the model)
        modelViewMatrix = getModelviewMatrix();

        // "project" the mesh vertices into the screen space.  This doesn't
        // actually draw anything, it just creates the screen-space xyz
        // coordinate for each vert.  From those values sortTriangles()
        // can sort the mesh triangles[] by Z depth (back to front).
        obj.projectVerts(obj, modelViewMatrix, projectionMatrix, viewport);
        obj.sortTriangles();

        // make depth buffer read-only before drawing translucent objects
        gl.glDepthMask(false);

        // draw the mesh (will draw back to front if triangles are sorted by Z)
    	gl.glEnable(GL.GL_TEXTURE_2D);
        clearMtl.apply();
        renderMesh(obj,marbleTextureHandle);

        // make depth buffer writable
        gl.glDepthMask(true);
    }

    /**
     * Render mesh with normals and texture coordinates.  Loops through
     * all triangles in the mesh object.
     *
     * Several triangles may refer to the same vertex, but each face
     * can have different normals for that vertex.  This allows for
     * sharp edges between faces.
     *
     * @param o  mesh object to render
     */
    public void renderMesh(JOMesh o, int textureHandle)
    {
        JOTriangle t;
        gl.glBindTexture(GL.GL_TEXTURE_2D,textureHandle);
        gl.glBegin(GL.GL_TRIANGLES);
        for (int j = 0; j < o.triangles.length; j++) { // draw all triangles in object
            t = o.triangles[j];

            gl.glTexCoord2f(t.uvw1.x, t.uvw1.y);
            gl.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z);
            gl.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z);

            gl.glTexCoord2f(t.uvw2.x, t.uvw2.y);
            gl.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z);
            gl.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z);

            gl.glTexCoord2f(t.uvw3.x, t.uvw3.y);
            gl.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z);
            gl.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z);
        }
        gl.glEnd();
    }

}
