суббота, 30 апреля 2011 г.

Jogl2.0. Начало.

Чтобы создать минимальное приложение нужно 2 класса – основной(с main()), реализующий/расширяющий GLCanvas и/или реализующий/расширяющий JFrame, [инициализирующий фрейм (окно)] и запускающий аниматора (Animator или FPSAnimator), и еще один класс, реализующий интерфейс GLEventListener. GLEventListener содержит в себе следующие методы для реализации:
  • init() – обычно включает в себя установку z-(глубинного) буфера, создание света, загрузку текстур и построение списка для отображения (для метода display()).
  • display() – в нем идет отображение;
  • reshape() – в нем происходят действия, связанные с перересовкой, например растянули окно или переместили его, что также включается при первоначальном рисовании на экране. Это делает этот метод стандартным местом для помещения в него команд OpenGL для установки области просмотра(viewport) и проекционной матрицы (projection matrix);
  • dispose() – в нем нужно производить действия, связанные с уничтожением объектов.

Возьмем пример с крутящимся кубом:


CubeGL.java

import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.jogamp.opengl.util.FPSAnimator;

public class CubeGL extends JFrame {
private FPSAnimator animator;
private GLCanvas canvas;
private JPanel renderPane;
private CubeGLListener listener;

public CubeGL(int fps) {
super("CubeGL (Basic)");
Container c = getContentPane();
c.add(makeRenderPanel(fps));
// добавим слушателя для выхода из программы (в другом трэде)
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
new Thread(new Runnable() {
public void run() {
if (animator.isAnimating())
animator.stop();
System.exit(0);
}
}).start();
}
});
pack();
animator.start();

setVisible(true);
canvas.requestFocus();
}

private JPanel makeRenderPanel(int fps) {
renderPane = new JPanel();
renderPane.setLayout(new BorderLayout());
// renderPane.setOpaque(false);

canvas = new GLCanvas(); // the canvas
canvas.setSize(new Dimension(300, 300));
// the listener
listener = new CubeGLListener();
canvas.addGLEventListener(listener);
// the animator uses fixed rate scheduling
animator = new FPSAnimator(canvas, fps, true);

renderPane.setSize(canvas.getPreferredSize());
renderPane.add(canvas, BorderLayout.CENTER);

return renderPane;
}

public static void main(String[] args) {
new CubeGL(60);
}
}



CubeGLListener.java
import java.util.Random;

import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

public class CubeGLListener implements GLEventListener{
private static final float INCR_MAX = 1.0f;   // rotation increments
private int cubeDList;   // display list for displaying the cube

// rotation variables
private float rotX, rotY, rotZ;     // total rotations in x,y,z axes
private float incrX, incrY, incrZ;  // increments for x,y,z rotations

private static final double Z_DIST = 7.0;    // for camera position

private float[][] verts = {
{-1.0f,-1.0f, 1.0f},  // vertex 0
{-1.0f, 1.0f, 1.0f},  // 1
{ 1.0f, 1.0f, 1.0f},  // 2
{ 1.0f,-1.0f, 1.0f},  // 3
{-1.0f,-1.0f,-1.0f},  // 4
{-1.0f, 1.0f,-1.0f},  // 5
{ 1.0f, 1.0f,-1.0f},  // 6
{ 1.0f,-1.0f,-1.0f},  // 7
};

@Override
public void display(GLAutoDrawable drawable) {
// TODO Auto-generated method stub
// update the rotations
rotX = (rotX + incrX) % 360.0f;
rotY = (rotY + incrY) % 360.0f;
rotZ = (rotZ + incrZ) % 360.0f;

GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();
// clear color and depth buffers
gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
glu.gluLookAt(0, 0, Z_DIST, 0, 0, 0, 0, 1, 0);   // position camera
// apply rotations to the x,y,z axes
gl.glRotatef(rotX, 1.0f, 0.0f, 0.0f);
gl.glRotatef(rotY, 0.0f, 1.0f, 0.0f);
gl.glRotatef(rotZ, 0.0f, 0.0f, 1.0f);
gl.glCallList(cubeDList);   //execute display list for drawing cube
}

@Override
public void dispose(GLAutoDrawable drawable) {
// TODO Auto-generated method stub

}

@Override
public void init(GLAutoDrawable drawable) {
// TODO Auto-generated method stub
GL2 gl = drawable.getGL().getGL2();    // don't make this gl a global!

// initialize the rotation variables
rotX = 0; rotY = 0; rotZ = 0;
Random random = new Random();
incrX = random.nextFloat()*INCR_MAX;   // 0 - INCR_MAX degrees
incrY = random.nextFloat()*INCR_MAX;
incrZ = random.nextFloat()*INCR_MAX;
gl.glClearColor(0.17f, 0.65f, 0.92f, 0.0f); //sky color background
// z- (depth) buffer initialization for hidden surface removal
gl.glEnable(GL2.GL_DEPTH_TEST);
// create a display list for drawing the cube
cubeDList = gl.glGenLists(1);
gl.glNewList(cubeDList, GL2.GL_COMPILE);
drawColourCube(gl);
gl.glEndList();
}

@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
// TODO Auto-generated method stub
GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();

if (height == 0)height = 1; // to avoid division by 0 in aspect ratio below
gl.glViewport(x, y, width, height);  // size of drawing area
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
// camera's angle of view, aspect ratio, near & far clipping planes
glu.gluPerspective(45.0, (float)width/(float)height, 1, 100);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
}
// six-sided cube, with a different color on each face
private void drawColourCube(GL2 gl) {
gl.glColor3f(1.0f, 0.0f, 0.0f);   // red
drawPolygon(gl, 0, 3, 2, 1);      // front face
gl.glColor3f(0.0f, 1.0f, 0.0f);   // green
drawPolygon(gl, 2, 3, 7, 6);      // right
gl.glColor3f(0.0f, 0.0f, 1.0f);   // blue
drawPolygon(gl, 3, 0, 4, 7);      // bottom
gl.glColor3f(1.0f, 1.0f, 0.0f);   // yellow
drawPolygon(gl, 1, 2, 6, 5);      // top
gl.glColor3f(0.0f, 1.0f, 1.0f);   // light blue
drawPolygon(gl, 4, 5, 6, 7);      // back
gl.glColor3f(1.0f, 0.0f, 1.0f);   // purple
drawPolygon(gl, 5, 4, 0, 1);      // left
}
// the polygon vertices come from the verts[] array
private void drawPolygon(GL2 gl, int vIdx0, int vIdx1, int vIdx2, int vIdx3) {
gl.glBegin(GL2.GL_POLYGON);
gl.glVertex3f(verts[vIdx0][0],verts[vIdx0][1], verts[vIdx0][2] );
gl.glVertex3f(verts[vIdx1][0],verts[vIdx1][1], verts[vIdx1][2] );
gl.glVertex3f(verts[vIdx2][0],verts[vIdx2][1], verts[vIdx2][2] );
gl.glVertex3f(verts[vIdx3][0],verts[vIdx3][1], verts[vIdx3][2] );
gl.glEnd();
}
}

Документация jogl предостерегает от использования глобального GL инстанса (объекта),
поскольку это искушает программистов вызывать функции OpenGL от мышей и клавиатур
или других тэдов.
И это приводит к краху приложения, поскольку контекст OpenGL
(его внутреннее состояние(?)) связано с GLEventListener'ом.
В то же время можно делать глобальным GLU, но  только для методов обратного вызова.

Вершины(verts) соответствуют точкам на рисунке:
Позиция вершины в массиве используется в методе drawPolygon() чтобы нарисовать кубик. В drawPolygon() после объявления начала рисования(GL.glBegin()) идет опция рисования полигона GL2.GL_POLYGON, существуют также опции рисования GL2.GL_POINTS, GL2.GL_LINES, вроде понятно что это.

(x, y) аргументы метода reshape() определяют позицию канваса относительно объемлющего его контейнера. В этом примере они (0,0). GL-объект заново создается из входного аргумента GLAutoDrawable. Вызов GL.glViewport() определяет размеры рисуемого окна, начиная с НИЖНЕГО ЛЕВОГО угла + ширина и высота. Режим матрицы переключается в проекцию (PROJECTION) gl.glMatrixMode(GL2.GL_PROJECTION) – определяет маппинг 3х мерной сцены в 2х мерную. gl.glLoadIdentity() – обнуляет матрицу. В конце режим матрицы переключается в MODELVIEW. Это определяет систему координат сцены, используемой при размещении или перемещении 3D объекта. Этот режим устанавливается вконце, так как следующим будет вызван display(), рисующий сцену.

Комментариев нет: