// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.GL.*;  
import com.jogamp.opengl.GL2.*; 
import com.jogamp.opengl.glu.GLU;

class Renderer {

  private GLU glu = new GLU();

  public float t = 0.0f;
  private int width = 0;
  private int height = 0;
      
  public void init(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    gl.glEnable(GL2.GL_DEPTH_TEST);
  }
  public void resize(GLAutoDrawable d, int w, int h) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    width = w;
    height = h;  
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    glu.gluPerspective (30.0, (double)w/(double)h, 2.0, 20.0);
  }
  
  public void display(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

    // top right viewport (look from front)
    gl.glViewport(width/2, height/2, width/2, height/2);
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    drawFrame(d);
    // set camera (look from positive x-direction)
    glu.gluLookAt(10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    drawSceneGrid(d);
    drawRotatingPyramid(d);

    // bottom left viewport (look from left)
    gl.glViewport(0, 0, width/2, height/2);
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    drawFrame(d);
     // set camera (look from negative y-direction)
    glu.gluLookAt(0.0, -10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    drawSceneGrid(d);
    drawRotatingPyramid(d);

    // top left viewport (look from top)
    gl.glViewport(0, height/2, width/2, height/2);
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    drawFrame(d);
    // set camera (look from positive z-direction)
    glu.gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0);
    // draw scene
    drawSceneGrid(d);
    drawRotatingPyramid(d);

    // bottom right viewport (perspective)
    gl.glViewport(width/2, 0, width/2, height/2);
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    drawFrame(d);
    // set camera
    glu.gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
    // draw scene
    drawSceneGrid(d);
    drawRotatingPyramid(d);
  }
 
  private void drawFrame(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glLineWidth(2.0f);
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glPushMatrix();
    gl.glLoadIdentity();
    gl.glColor3f(1.0f, 1.0f, 1.0f);
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f(-1.0f, 1.0f, 0.0f);
    gl.glVertex3f( 1.0f, 1.0f, 0.0f);
    gl.glVertex3f( 1.0f,-1.0f, 0.0f);
    gl.glVertex3f(-1.0f,-1.0f, 0.0f);
    gl.glEnd();
    gl.glPopMatrix();
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLineWidth(1.0f);
}

  private void drawSceneGrid(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context

    gl.glColor3f(0.3f, 0.3f, 0.3f);
    gl.glBegin(GL2.GL_LINES);
    for(int i=0; i<=10; i++) {
      gl.glVertex3f(-5.0f+i, -5.0f,   0.0f);
      gl.glVertex3f(-5.0f+i,  5.0f,   0.0f);
      gl.glVertex3f(-5.0f,   -5.0f+i, 0.0f);
      gl.glVertex3f( 5.0f,   -5.0f+i, 0.0f);
    }
    gl.glEnd();  

    gl.glColor3f(0.0f, 0.0f, 1.0f);
    drawCoordinateAxisZ(d);
    gl.glColor3f(0.0f, 1.0f, 0.0f);
    drawCoordinateAxisY(d);
    gl.glColor3f(1.0f, 0.0f, 0.0f);
    drawCoordinateAxisX(d);
  }

  private void drawCoordinateAxisZ(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    
    gl.glLineWidth(2.0f);
    gl.glBegin(GL2.GL_LINES);
    gl.glVertex3f(0.0f, 0.0f, 0.0f); // z-axis
    gl.glVertex3f(0.0f, 0.0f, 2.0f);
    gl.glEnd();
    gl.glLineWidth(1.0f);

    // z-axis tip
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f, 0.0f, 2.0f);
    gl.glVertex3f(-0.05f, 0.05f, 1.9f);
    gl.glVertex3f( 0.05f, 0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f( 0.05f,  0.05f, 1.9f);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glVertex3f(-0.05f,  0.05f, 1.9f);
    gl.glEnd();
    gl.glBegin(GL2.GL_POLYGON);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.05f,  0.05f, 1.9f);
    gl.glVertex3f(-0.05f,  0.05f, 1.9f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glEnd();
  }  
  
  private void drawCoordinateAxisX(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    drawCoordinateAxisZ(d);
    gl.glPopMatrix();
  }
  
  private void drawCoordinateAxisY(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glPushMatrix();
    gl.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
    drawCoordinateAxisZ(d);
    gl.glPopMatrix();
  }
  
  private void drawRotatingPyramid(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glRotatef(t, 0.0f, 0.0f, 1.0f);
    drawPyramid(d);
  }
  
  private void drawPyramid(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glColor3f(1.0f,0.0f,0.0f);
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f, 0.0f, 1.5f);
    gl.glVertex3f(-1.0f, 1.0f, 0.0f);
    gl.glVertex3f( 1.0f, 1.0f, 0.0f);
    gl.glEnd();
    gl.glColor3f(0.0f,1.0f,0.0f);
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f,  0.0f, 1.5f);
    gl.glVertex3f( 1.0f, -1.0f, 0.0f);
    gl.glVertex3f(-1.0f, -1.0f, 0.0f);
    gl.glEnd();
    gl.glColor3f(0.0f,0.0f,1.0f);
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f,  0.0f, 1.5f);
    gl.glVertex3f( 1.0f,  1.0f, 0.0f);
    gl.glVertex3f( 1.0f, -1.0f, 0.0f);
    gl.glEnd();
    gl.glColor3f(1.0f,1.0f,0.0f);
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f,  0.0f, 1.5f);
    gl.glVertex3f(-1.0f, -1.0f, 0.0f);
    gl.glVertex3f(-1.0f,  1.0f, 0.0f);
    gl.glEnd();
    gl.glColor3f(0.0f,1.0f,1.0f);
    gl.glBegin(GL2.GL_POLYGON);
    gl.glVertex3f( 1.0f, -1.0f, 0.0f);
    gl.glVertex3f( 1.0f,  1.0f, 0.0f);
    gl.glVertex3f(-1.0f,  1.0f, 0.0f);
    gl.glVertex3f(-1.0f, -1.0f, 0.0f);
    gl.glEnd();
  }
}

class MyGui extends JFrame implements GLEventListener {

  private Renderer renderer;
  
  public void createGUI() {
    setTitle("glViewport Demo");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   
    GLProfile glp = GLProfile.getDefault();
    GLCapabilities caps = new GLCapabilities(glp);
    GLCanvas canvas = new GLCanvas(caps);
    setSize(320, 320);
    
    getContentPane().add(canvas);
    final FPSAnimator ani = new FPSAnimator(canvas, 60, true);
    canvas.addGLEventListener(this);
    setVisible(true);
    renderer = new Renderer();

    ani.start();
  }

  @Override
  public void init(GLAutoDrawable d) { 
    renderer.init(d); 
  }

  @Override
  public void reshape(GLAutoDrawable d, int x, int y, int width, int height) {
    renderer.resize(d, width, height);
  }

  @Override
  public void display(GLAutoDrawable d) {
    float offset = 1.0f;
    renderer.t += offset;
    renderer.display(d);
  }

  @Override
  public void dispose(GLAutoDrawable d) { 
  }

}

public class Viewport {
  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        MyGui myGUI = new MyGui();
        myGUI.createGUI();
      }
    });
  }
}
