package cz.urbangaming.galgs;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import android.opengl.GLES20;
import android.util.Log;
import android.util.Pair;
import cz.urbangaming.galgs.algorithms.convexhull.GiftWrapping;
import cz.urbangaming.galgs.algorithms.convexhull.GrahamScan;
import cz.urbangaming.galgs.algorithms.kdtree.KDTree;
import cz.urbangaming.galgs.algorithms.triangulation.SweepTriangulation;
import cz.urbangaming.galgs.utils.Point2D;
import cz.urbangaming.galgs.utils.Utils;
/**
*
* @author Michal Karm Babacek
* @license GNU GPL 3.0
*
*/
public class Scene {
private static int counter = 0;
private RubyAlgorithms rubyAlgorithms = null;
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" gl_PointSize = " + GAlg.POINT_SIZE + ";" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final int mProgram;
private final int mLinesProgram;
private int mPositionHandle;
private int mLinesPositionHandle;
private int mColorHandle;
private int mLinesColorHandle;
private FloatBuffer vertexBuffer = null;
private FloatBuffer linesVertexBuffer = null;
public static final int COORDS_PER_VERTEX = 3;
private List<Point2D> verticesCoords = new ArrayList<Point2D>();
private List<Point2D> linesCoords = new ArrayList<Point2D>();
private int vertexCount = 0;
private int linesVertexCount = 0;
private final int VERTEX_STRIDE = COORDS_PER_VERTEX * 4; // bytes per vertex
private static final float colorVertices[] = { 1f, 0f, 0f, 1.0f };
private static final float colorLines[] = { 0f, 1f, 0f, 1.0f };
private boolean drawLines = false;
private PointsRenderer pointsRenderer = null;
private GAlg galg = null;
// -1 means "none selected" X Y Z index
private int selectedVertexIndex = -1;
private boolean vertexSelected = false;
private int renderMethod = GLES20.GL_LINE_LOOP;
public Scene(PointsRenderer pointsRenderer, GAlg galg) {
this.pointsRenderer = pointsRenderer;
this.rubyAlgorithms = new RubyAlgorithms();
this.galg = galg;
newVertexBufferToDraw();
// prepare shaders and OpenGL program
int vertexShader = PointsRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = PointsRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
mLinesProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
// Hmm, let'suse the same shaders for this. TODO: Really?
// TODO: This is weird... :-(
GLES20.glAttachShader(mLinesProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mLinesProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mLinesProgram); // create OpenGL program executables
}
public void clearScene() {
verticesCoords.clear();
linesCoords.clear();
drawLines = false;
newVertexBufferToDraw();
}
public void addRandomPoints() {
verticesCoords.addAll(Utils.generateSomeVertices(GAlg.HOW_MANY_POINTS_GENERATE,
GAlg.BORDER_POINT_POSITION,
GAlg.BORDER_POINT_POSITION,
pointsRenderer.getSurfaceWidth() - GAlg.BORDER_POINT_POSITION,
pointsRenderer.getSurfaceHeight() - GAlg.BORDER_POINT_POSITION));
newVertexBufferToDraw();
}
public void addVertex(Point2D point2d) {
verticesCoords.add(point2d);
newVertexBufferToDraw();
}
public void selectVertex(Point2D point2d) {
if (!vertexSelected) {
// Brutal force :-)
for (int i = 0; i < verticesCoords.size(); i++) {
if (Utils.isInRectangle(point2d, GAlg.FINGER_ACCURACY, verticesCoords.get(i))) {
selectedVertexIndex = i;
vertexSelected = true;
break;
}
}
}
}
public void moveSelectedVertexTo(float x, float y) {
if (vertexSelected) {
// Update location x,y
// Really? Isn't it a copy? Might be an epic one... #fail
verticesCoords.get(selectedVertexIndex).updateWith(x, y);
newVertexBufferToDraw();
}
}
public void deselectVertex() {
vertexSelected = false;
selectedVertexIndex = -1;
}
public void removeVertex(Point2D point2d) {
List<Point2D> newSceneCoords = verticesCoords;
// Brutal force...
//TODO: Divide search space
for (int i = 0; i < verticesCoords.size(); i++) {
if (Utils.isInRectangle(point2d, GAlg.FINGER_ACCURACY, verticesCoords.get(i))) {
newSceneCoords.remove(i);
break;
}
}
verticesCoords = newSceneCoords;
newVertexBufferToDraw();
}
public void renderLines(int algorithmUsed) {
linesCoords.clear();
Pair<List<Point2D>, Integer> results = null;
long time = System.currentTimeMillis();
switch (algorithmUsed) {
case GAlg.CONVEX_HULL_GW:
results = GiftWrapping.getInstance().convexHullGiftWrapping(verticesCoords);
break;
case GAlg.CONVEX_HULL_GS:
results = GrahamScan.getInstance().convexHullGrahamScan(verticesCoords);
break;
case GAlg.LINKED_POINTS:
results = new Pair<List<Point2D>, Integer>(verticesCoords, GLES20.GL_LINE_LOOP);
break;
case GAlg.SWEEP_TRIANGULATION:
results = SweepTriangulation.getInstance().sweepTriangulation(verticesCoords);
break;
case GAlg.NAIVE_TRIANGULATION:
results = SweepTriangulation.getInstance().naiveTriangulation(verticesCoords);
break;
case GAlg.KD_TREE:
results = new KDTree(pointsRenderer.getSurfaceWidth(), pointsRenderer.getSurfaceHeight()).buildKDTree(verticesCoords);
break;
default:
if (galg.getRubyMethods().containsKey(algorithmUsed)) {
results = rubyAlgorithms.manipulateSceneWithRuby(verticesCoords, "galgs_" + galg.getRubyMethods().get(algorithmUsed));
}
break;
}
renderMethod = (results != null) ? results.second : /* default */GLES20.GL_POINTS;
Log.d(GAlg.DEBUG_TAG, "#" + counter + "TIME TAKEN:" + (System.currentTimeMillis() - time));
counter++;
// Doesn't make any sense with less than 2 vertices.
if (results != null && results.first != null && results.first.size() >= 2) {
linesCoords.addAll(results.first);
drawLines = true;
newVertexBufferToDraw();
}
}
private void newVertexBufferToDraw() {
// (number of points) * (number of coordinate values) * 4 bytes per float)
ByteBuffer bb = ByteBuffer.allocateDirect(verticesCoords.size() * COORDS_PER_VERTEX * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(Utils.pointVectorToArray(verticesCoords));
// set the buffer to read the first coordinate
vertexBuffer.position(0);
vertexCount = verticesCoords.size();
// Log.d(GAlg.DEBUG_TAG, "Scene coords to draw: " + verticesCoords.toString());
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexCount, vertexBuffer);
if (drawLines) {
bb = ByteBuffer.allocateDirect(linesCoords.size() * COORDS_PER_VERTEX * 4);
bb.order(ByteOrder.nativeOrder());
linesVertexBuffer = bb.asFloatBuffer();
linesVertexBuffer.put(Utils.pointVectorToArray(linesCoords));
linesVertexBuffer.position(0);
linesVertexCount = linesCoords.size();
Log.d(GAlg.DEBUG_TAG, "Drawing lines between: " + linesCoords.toString());
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, linesVertexCount, linesVertexBuffer);
}
}
public void draw() {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the scene vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the scene coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, VERTEX_STRIDE, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set colorVertices for drawing the scene
GLES20.glUniform4fv(mColorHandle, 1, colorVertices, 0);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, pointsRenderer.mtrxProjectionAndView, 0);
// Draw the scene
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vertexCount);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
if (drawLines) {
GLES20.glUseProgram(mLinesProgram);
mLinesPositionHandle = GLES20.glGetAttribLocation(mLinesProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mLinesPositionHandle);
// Prevent null pointer race condition
if (linesVertexBuffer != null) {
GLES20.glVertexAttribPointer(mLinesPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, VERTEX_STRIDE, linesVertexBuffer);
mLinesColorHandle = GLES20.glGetUniformLocation(mLinesProgram, "vColor");
GLES20.glUniform4fv(mLinesColorHandle, 1, colorLines, 0);
int mtrxLineshandle = GLES20.glGetUniformLocation(mLinesProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mtrxLineshandle, 1, false, pointsRenderer.mtrxProjectionAndView, 0);
//GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, linesVertexCount);
//GLES20.glDrawArrays(GLES20.GL_LINES, 0, linesVertexCount);
//GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, linesVertexCount);
//GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, linesVertexCount);
GLES20.glDrawArrays(renderMethod, 0, linesVertexCount);
GLES20.glDisableVertexAttribArray(mLinesPositionHandle);
}
}
}
}