package demos.nehe.lesson36;

import com.sun.opengl.util.BufferUtil;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.DebugGL;
import javax.media.opengl.glu.GLU;
import java.nio.ByteBuffer;

import demos.common.GLDisplay;

class Renderer implements GLEventListener {
    private int TEXTURE_SIZE = 128;

    private static final int x = 0;                                        // Define X Coord
    private static final int y = 1;                                        // Define Y Coord
    private static final int z = 2;                                        // Define Z Coord

// User Defined Variables
    private float angle; // Used To Rotate The Helix
    private float[][] vertexes = new float[4][3]; // Holds Float Info For 4 Sets Of Vertices
    private float[] normal = new float[3]; // An Array To Store The Normal Data
    private int blurTexture; // An Unsigned Int To Store The Texture Number
    private long previousTime = System.currentTimeMillis();

    private GLU glu = new GLU();

    private int frameBufferObject;
    private GLDisplay display;

    public Renderer(GLDisplay display) {
        this.display = display;
    }

    private int createBlurTexture(GL gl) {                                // Create An Empty Texture
        ByteBuffer data = BufferUtil.newByteBuffer(TEXTURE_SIZE * TEXTURE_SIZE); // Create Storage Space For Texture Data (128x128x4)
        data.limit(data.capacity());

        int[] txtnumber = new int[1];
        gl.glGenTextures(1, txtnumber, 0);                                // Create 1 Texture
        gl.glBindTexture(GL.GL_TEXTURE_2D, txtnumber[0]);                 // Bind The Texture
        gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_LUMINANCE, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, data);                        // Build Texture Using Information In data
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

        return txtnumber[0];                                              // Return The Texture ID
    }

    private void normalize(float[] vector) {                             // Normalizes A Vector (3 Coordinates)
        // To A Unit Normal Vector With A Length Of One.
        // Calculates The Length Of The Vector
        float length = (float) Math.sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2]));

        if (length == 0.0f)                                               // Prevents Divide By 0 Error By Providing
        {
            length = 1.0f;                                                // An Acceptable Value For Vectors To Close To 0.
        }

        vector[0] /= length;                                              // Dividing Each Element By
        vector[1] /= length;                                              // The Length Results In A
        vector[2] /= length;                                              // Unit Normal Vector.
    }

    private void calcNormal(float[][] v/*[3][3]*/, float[] out/*[3]*/) {  // Calculates Normal For A Quad Using 3 Points
        float[] v1 = new float[3];
        float[] v2 = new float[3];                                        // Vector 1 (x,y,z) & Vector 2 (x,y,z)

        // Finds The Vector Between 2 Points By Subtracting
        // The x,y,z Coordinates From One Point To Another.

        // Calculate The Vector From Point 1 To Point 0
        v1[x] = v[0][x] - v[1][x];                                        // Vector 1.x=Vertex[0].x-Vertex[1].x
        v1[y] = v[0][y] - v[1][y];                                        // Vector 1.y=Vertex[0].y-Vertex[1].y
        v1[z] = v[0][z] - v[1][z];                                        // Vector 1.z=Vertex[0].y-Vertex[1].z
        // Calculate The Vector From Point 2 To Point 1
        v2[x] = v[1][x] - v[2][x];                                        // Vector 2.x=Vertex[0].x-Vertex[1].x
        v2[y] = v[1][y] - v[2][y];                                        // Vector 2.y=Vertex[0].y-Vertex[1].y
        v2[z] = v[1][z] - v[2][z];                                        // Vector 2.z=Vertex[0].z-Vertex[1].z
        // Compute The Cross Product To Give Us A Surface Normal
        out[x] = v1[y] * v2[z] - v1[z] * v2[y];                           // Cross Product For Y - Z
        out[y] = v1[z] * v2[x] - v1[x] * v2[z];                           // Cross Product For X - Z
        out[z] = v1[x] * v2[y] - v1[y] * v2[x];                           // Cross Product For X - Y

        normalize(out);                                                // Normalize The Vectors
    }

    private void processHelix(GL gl, GLU glu) {                           // Draws A Helix
        float x;                                                          // Helix x Coordinate
        float y;                                                          // Helix y Coordinate
        float z;                                                          // Helix z Coordinate
        float phi;                                                        // Angle
        float theta;                                                      // Angle
        float v, u;                                                       // Angles
        float r;                                                          // Radius Of Twist
        int twists = 5;                                                   // 5 Twists

        float[] glfMaterialColor = new float[]{0.4f, 0.2f, 0.8f, 1.0f};  // Set The Material Color
        float[] specular = new float[]{1.0f, 1.0f, 1.0f, 1.0f};          // Sets Up Specular Lighting

        gl.glLoadIdentity();                                             // Reset The Modelview Matrix
        glu.gluLookAt(0, 5, 50, 0, 0, 0, 0, 1, 0);                       // Eye Position (0,5,50) Center Of Scene (0,0,0), Up On Y Axis

        gl.glPushMatrix();                                               // Push The Modelview Matrix

        gl.glTranslatef(0, 0, -50);                                      // Translate 50 Units Into The Screen
        gl.glRotatef(angle / 2.0f, 1, 0, 0);                             // Rotate By angle/2 On The X-Axis
        gl.glRotatef(angle / 3.0f, 0, 1, 0);                             // Rotate By angle/3 On The Y-Axis

        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, glfMaterialColor, 0);
        gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, specular, 0);

        r = 1.5f;                                                        // Radius

        gl.glBegin(GL.GL_QUADS);                                         // Begin Drawing Quads
        for (phi = 0; phi <= 360; phi += 20.0)                           // 360 Degrees In Steps Of 20
        {
            for (theta = 0; theta <= 360 * twists; theta += 20.0)        // 360 Degrees * Number Of Twists In Steps Of 20
            {
                v = (phi / 180.0f * 3.142f);                             // Calculate Angle Of First Point	(  0 )
                u = (theta / 180.0f * 3.142f);                           // Calculate Angle Of First Point	(  0 )

                x = (float) (Math.cos(u) * (2.0f + Math.cos(v))) * r;    // Calculate x Position (1st Point)
                y = (float) (Math.sin(u) * (2.0f + Math.cos(v))) * r;    // Calculate y Position (1st Point)
                z = (float) (((u - (2.0f * 3.142f)) + Math.sin(v)) * r); // Calculate z Position (1st Point)

                vertexes[0][0] = x;                                      // Set x Value Of First Vertex
                vertexes[0][1] = y;                                      // Set y Value Of First Vertex
                vertexes[0][2] = z;                                      // Set z Value Of First Vertex

                v = (phi / 180.0f * 3.142f);                             // Calculate Angle Of Second Point	(  0 )
                u = ((theta + 20) / 180.0f * 3.142f);                    // Calculate Angle Of Second Point	( 20 )

                x = (float) (Math.cos(u) * (2.0f + Math.cos(v))) * r;    // Calculate x Position (2nd Point)
                y = (float) (Math.sin(u) * (2.0f + Math.cos(v))) * r;    // Calculate y Position (2nd Point)
                z = (float) (((u - (2.0f * 3.142f)) + Math.sin(v)) * r); // Calculate z Position (2nd Point)

                vertexes[1][0] = x;                                      // Set x Value Of Second Vertex
                vertexes[1][1] = y;                                      // Set y Value Of Second Vertex
                vertexes[1][2] = z;                                      // Set z Value Of Second Vertex

                v = ((phi + 20) / 180.0f * 3.142f);                      // Calculate Angle Of Third Point	( 20 )
                u = ((theta + 20) / 180.0f * 3.142f);                    // Calculate Angle Of Third Point	( 20 )

                x = (float) (Math.cos(u) * (2.0f + Math.cos(v))) * r;    // Calculate x Position (3rd Point)
                y = (float) (Math.sin(u) * (2.0f + Math.cos(v))) * r;    // Calculate y Position (3rd Point)
                z = (float) (((u - (2.0f * 3.142f)) + Math.sin(v)) * r); // Calculate z Position (3rd Point)

                vertexes[2][0] = x;                                      // Set x Value Of Third Vertex
                vertexes[2][1] = y;                                      // Set y Value Of Third Vertex
                vertexes[2][2] = z;                                      // Set z Value Of Third Vertex

                v = ((phi + 20) / 180.0f * 3.142f);                      // Calculate Angle Of Fourth Point	( 20 )
                u = ((theta) / 180.0f * 3.142f);                         // Calculate Angle Of Fourth Point	(  0 )

                x = (float) (Math.cos(u) * (2.0f + Math.cos(v))) * r;    // Calculate x Position (4th Point)
                y = (float) (Math.sin(u) * (2.0f + Math.cos(v))) * r;    // Calculate y Position (4th Point)
                z = (float) (((u - (2.0f * 3.142f)) + Math.sin(v)) * r); // Calculate z Position (4th Point)

                vertexes[3][0] = x;                                      // Set x Value Of Fourth Vertex
                vertexes[3][1] = y;                                      // Set y Value Of Fourth Vertex
                vertexes[3][2] = z;                                      // Set z Value Of Fourth Vertex

                calcNormal(vertexes, normal);                            // Calculate The Quad Normal

                gl.glNormal3f(normal[0], normal[1], normal[2]);          // Set The Normal

                // Render The Quad
                gl.glVertex3f(vertexes[0][0], vertexes[0][1], vertexes[0][2]);
                gl.glVertex3f(vertexes[1][0], vertexes[1][1], vertexes[1][2]);
                gl.glVertex3f(vertexes[2][0], vertexes[2][1], vertexes[2][2]);
                gl.glVertex3f(vertexes[3][0], vertexes[3][1], vertexes[3][2]);
            }
        }
        gl.glEnd();                                                      // Done Rendering Quads

        gl.glPopMatrix();                                                // Pop The Matrix
    }

    private void viewOrtho(GL gl)                                        // Set Up An Ortho View
    {
        gl.glMatrixMode(GL.GL_PROJECTION);                               // Select Projection
        gl.glPushMatrix();                                               // Push The Matrix
        gl.glLoadIdentity();                                             // Reset The Matrix
        gl.glOrtho(0, 640, 480, 0, -1, 1);                               // Select Ortho Mode (640x480)
        gl.glMatrixMode(GL.GL_MODELVIEW);                                // Select Modelview Matrix
        gl.glPushMatrix();                                               // Push The Matrix
        gl.glLoadIdentity();                                             // Reset The Matrix
    }

    private void viewPerspective(GL gl)                                  // Set Up A Perspective View
    {
        gl.glMatrixMode(GL.GL_PROJECTION);                               // Select Projection
        gl.glPopMatrix();                                                // Pop The Matrix
        gl.glMatrixMode(GL.GL_MODELVIEW);                                // Select Modelview
        gl.glPopMatrix();                                                // Pop The Matrix
    }

    /**
     * Renders To A Texture
     */
    private void renderToTexture(GL gl, GLU glu)
    {
        if (frameBufferObject != -1) {
            // Bind the fbo
            gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, frameBufferObject);
        }

        // Set Our Viewport (Match Texture Size)
        gl.glViewport(0, 0, TEXTURE_SIZE, TEXTURE_SIZE);

        // Clear the frame buffer (either the default frame buffer or the fbo)
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Render The Helix
        processHelix(gl, glu);

        // Copy Our ViewPort To The Blur Texture (From 0,0 To 128,128... No Border)
        gl.glBindTexture(GL.GL_TEXTURE_2D, blurTexture);
        gl.glCopyTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_LUMINANCE, 0, 0, TEXTURE_SIZE, TEXTURE_SIZE, 0);

        if (frameBufferObject != -1) {
            // If we used the fbo, restore the default frame buffer
            gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0);
        }

        // Restore the viewport (0,0 to 640x480)
        gl.glViewport(0, 0, 640, 480);
    }

    private void drawBlur(GL gl, int times, float inc)           // Draw The Blurred Image
    {
        float spost = 0.0f;                                      // Starting Texture Coordinate Offset
        float alpha = 0.2f;                                      // Starting Alpha Value

        // Disable AutoTexture Coordinates
        gl.glDisable(GL.GL_TEXTURE_GEN_S);
        gl.glDisable(GL.GL_TEXTURE_GEN_T);

        gl.glEnable(GL.GL_TEXTURE_2D);                           // Enable 2D Texture Mapping
        gl.glDisable(GL.GL_DEPTH_TEST);                          // Disable Depth Testing
        gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);              // Set Blending Mode
        gl.glEnable(GL.GL_BLEND);                                // Enable Blending
        gl.glBindTexture(GL.GL_TEXTURE_2D, blurTexture);         // Bind To The Blur Texture
        viewOrtho(gl);                                           // Switch To An Ortho View

        float alphainc = alpha / times;                          // alphainc=0.2f / Times To Render Blur

        gl.glBegin(GL.GL_QUADS);                                 // Begin Drawing Quads
        for (int num = 0; num < times; num++)                    // Number Of Times To Render Blur
        {
            gl.glColor4f(1.0f, 1.0f, 1.0f, alpha);               // Set The Alpha Value (Starts At 0.2)
            gl.glTexCoord2f(0 + spost, 1 - spost);               // Texture Coordinate	( 0, 1 )
            gl.glVertex2f(0, 0);                                 // First Vertex		(   0,   0 )

            gl.glTexCoord2f(0 + spost, 0 + spost);               // Texture Coordinate	( 0, 0 )
            gl.glVertex2f(0, 480);                               // Second Vertex	(   0, 480 )

            gl.glTexCoord2f(1 - spost, 0 + spost);               // Texture Coordinate	( 1, 0 )
            gl.glVertex2f(640, 480);                             // Third Vertex		( 640, 480 )

            gl.glTexCoord2f(1 - spost, 1 - spost);               // Texture Coordinate	( 1, 1 )
            gl.glVertex2f(640, 0);                               // Fourth Vertex	( 640,   0 )

            spost += inc;                                        // Gradually Increase spost (Zooming Closer To Texture Center)
            alpha = alpha - alphainc;                            // Gradually Decrease alpha (Gradually Fading Image Out)
        }
        gl.glEnd();                                              // Done Drawing Quads

        viewPerspective(gl);                                     // Switch To A Perspective View

        gl.glEnable(GL.GL_DEPTH_TEST);                           // Enable Depth Testing
        gl.glDisable(GL.GL_TEXTURE_2D);                          // Disable 2D Texture Mapping
        gl.glDisable(GL.GL_TEXTURE_2D);                          // Disable 2D Texture Mapping
        gl.glDisable(GL.GL_TEXTURE_2D);                          // Disable 2D Texture Mapping
        gl.glDisable(GL.GL_BLEND);                               // Disable Blending
        gl.glBindTexture(GL.GL_TEXTURE_2D, 0);                   // Unbind The Blur Texture
    }

    public void init(GLAutoDrawable drawable) {
        drawable.setGL(new DebugGL(drawable.getGL()));

        GL gl = drawable.getGL();

        // Start Of User Initialization
        angle = 0.0f;                                            // Set Starting Angle To Zero

        // Create Our Empty Texture
        blurTexture = createBlurTexture(gl);

        if (gl.isExtensionAvailable("GL_EXT_framebuffer_object")) {
            frameBufferObject = createFrameBufferObject(gl);
        } else {
            frameBufferObject = -1;
        }

        if (frameBufferObject != -1) {
            display.setTitle(display.getTitle() + " using frame buffer object");
        } else {
            display.setTitle(display.getTitle() + " using default frame buffer");
        }

        gl.glEnable(GL.GL_DEPTH_TEST);                                  // Enable Depth Testing

        float[] global_ambient = new float[]{0.2f, 0.2f, 0.2f, 1.0f};   // Set Ambient Lighting To Fairly Dark Light (No Color)
        float[] light0pos = new float[]{0.0f, 5.0f, 10.0f, 1.0f};       // Set The Light Position
        float[] light0ambient = new float[]{0.2f, 0.2f, 0.2f, 1.0f};    // More Ambient Light
        float[] light0diffuse = new float[]{0.3f, 0.3f, 0.3f, 1.0f};    // Set The Diffuse Light A Bit Brighter
        float[] light0specular = new float[]{0.8f, 0.8f, 0.8f, 1.0f};   // Fairly Bright Specular Lighting

        float[] lmodel_ambient = new float[]{0.2f, 0.2f, 0.2f, 1.0f};   // And More Ambient Light
        gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, lmodel_ambient, 0);// Set The Ambient Light Model

        gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, global_ambient, 0);// Set The Global Ambient Light Model
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light0pos, 0);       // Set The Lights Position
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light0ambient, 0);    // Set The Ambient Light
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light0diffuse, 0);    // Set The Diffuse Light
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light0specular, 0);  // Set Up Specular Lighting
        gl.glEnable(GL.GL_LIGHTING);                                    // Enable Lighting
        gl.glEnable(GL.GL_LIGHT0);                                      // Enable Light0

        gl.glShadeModel(GL.GL_SMOOTH);                                  // Select Smooth Shading

        gl.glMateriali(GL.GL_FRONT, GL.GL_SHININESS, 128);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                        // Set The Clear Color To Black
    }

    /**
     * Creates a frame buffer object.
     * @return the newly created frame buffer object is or -1 if a frame buffer object could not be created
     */
    private int createFrameBufferObject(GL gl) {
        // Create the FBO
        int[] frameBuffer = new int[1];
        gl.glGenFramebuffersEXT(1, frameBuffer, 0);
        gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, frameBuffer[0]);

        // Create a TEXTURE_SIZE x TEXTURE_SIZE RGBA texture that will be used as color attachment
        // for the fbo.
        int[] colorBuffer = new int[1];
        gl.glGenTextures(1, colorBuffer, 0);                 // Create 1 Texture
        gl.glBindTexture(GL.GL_TEXTURE_2D, colorBuffer[0]);  // Bind The Texture
        gl.glTexImage2D(                                     // Build Texture Using Information In data
                                                             GL.GL_TEXTURE_2D,
                                                             0,
                                                             GL.GL_RGBA,
                                                             TEXTURE_SIZE,
                                                             TEXTURE_SIZE,
                                                             0,
                                                             GL.GL_RGBA,
                                                             GL.GL_UNSIGNED_BYTE,
                                                             BufferUtil.newByteBuffer(TEXTURE_SIZE * TEXTURE_SIZE * 4)
        );
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

        // Attach the texture to the frame buffer as the color attachment. This
        // will cause the results of rendering to the FBO to be written in the blur texture.
        gl.glFramebufferTexture2DEXT(
                GL.GL_FRAMEBUFFER_EXT,
                GL.GL_COLOR_ATTACHMENT0_EXT,
                GL.GL_TEXTURE_2D,
                colorBuffer[0],
                0
        );

        gl.glBindTexture(GL.GL_TEXTURE_2D, 0);

        // Create a 24-bit TEXTURE_SIZE x TEXTURE_SIZE depth buffer for the FBO.
        // We need this to get correct rendering results.
        int[] depthBuffer = new int[1];
        gl.glGenRenderbuffersEXT(1, depthBuffer, 0);
        gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, depthBuffer[0]);
        gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT24, TEXTURE_SIZE, TEXTURE_SIZE);

        // Attach the newly created depth buffer to the FBO.
        gl.glFramebufferRenderbufferEXT(
                GL.GL_FRAMEBUFFER_EXT,
                GL.GL_DEPTH_ATTACHMENT_EXT,
                GL.GL_RENDERBUFFER_EXT,
                depthBuffer[0]
        );

        // Make sure the framebuffer object is complete (i.e. set up correctly)
        int status = gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT);
        if (status == GL.GL_FRAMEBUFFER_COMPLETE_EXT) {
            return frameBuffer[0];
        } else {
            // No matter what goes wrong, we simply delete the frame buffer object
            // This switch statement simply serves to list all possible error codes
            switch(status) {
                case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
                    // One of the attachments is incomplete
                case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
                    // Not all attachments have the same size
                case GL.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
                    // The desired read buffer has no attachment
                case GL.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
                    // The desired draw buffer has no attachment
                case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
                    // Not all color attachments have the same internal format
                case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
                    // No attachments have been attached
                case GL.GL_FRAMEBUFFER_UNSUPPORTED_EXT:
                    // The combination of internal formats is not supported
                case GL.GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
                    // This value is no longer in the EXT_framebuffer_object specification
                default:
                    // Delete the color buffer texture
                    gl.glDeleteTextures(1, colorBuffer, 0);
                    // Delete the depth buffer
                    gl.glDeleteRenderbuffersEXT(1, depthBuffer, 0);
                    // Delete the FBO
                    gl.glDeleteFramebuffersEXT(1, frameBuffer, 0);
                    return -1;
            }
        }
    }

    private void update(long milliseconds) {                            // Perform Motion Updates Here
        angle += (float) (milliseconds) / 5.0f;                         // Update angle Based On The Clock
    }

    public void display(GLAutoDrawable drawable) {
        long currentTime = System.currentTimeMillis();
        update(currentTime - previousTime);
        previousTime = currentTime;

        GL gl = drawable.getGL();
        gl.glLoadIdentity();                                            // Reset The View
        renderToTexture(gl, glu);                                       // Render To A Texture

        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                        // Set The Clear Color To Black
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);    // Clear Screen And Depth Buffer
        processHelix(gl, glu);                                          // Draw Our Helix

        drawBlur(gl, 25, 0.02f);                                        // Draw The Blur Effect
        gl.glFlush();                                                   // Flush The GL Rendering Pipeline
    }

    public void reshape(GLAutoDrawable drawable,
                        int xstart,
                        int ystart,
                        int width,
                        int height) {
        GL gl = drawable.getGL();

        height = (height == 0) ? 1 : height;

        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();

        glu.gluPerspective(50, (float) width / height, 5, 2000);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void displayChanged(GLAutoDrawable drawable,
                               boolean modeChanged,
                               boolean deviceChanged) {
    }
}