OpenGL doesn't draw with my own matrix - java

I am trying to make a 2D game with opengl.I made the the class which draw meshes.And i made my own Matrix4f class.I have experience with 3D opengl.
When i pass my matrix4f to the shader it doesn't draw.But when i remove it from multiplying with the position the triangle appears on the screen.I tried my Matrix4f from my 3D Game Engine but it doesn't work as well.
Matrix4f class
package com.game.main.maths;
import java.nio.FloatBuffer;
import com.game.main.util.Util;
public class Matrix4f {
private float[][] m;
public Matrix4f() {
m = new float[4][4];
initIdentity();
}
public Matrix4f initIdentity() {
for (int x = 0; x < 4; x++)
for (int y = 0; y < 4; y++) {
if (x == y)
m[x][y] = 1.0f;
else
m[x][y] = 0;
}
return this;
}
public Matrix4f translate(float x, float y, float z) {
initIdentity();
m[0][3] = x;
m[1][3] = y;
m[2][3] = z;
return this;
}
public Matrix4f translate(Vector3f pos) {
initIdentity();
m[0][3] = pos.getX();
m[1][3] = pos.getY();
m[2][3] = pos.getZ();
return this;
}
public Matrix4f rotate(float angle) {
initIdentity();
float rad = (float) Math.toRadians(angle);
float cos = (float) Math.cos(rad);
float sin = (float) Math.sin(rad);
m[0][0] = cos;
m[1][0] = sin;
m[0][1] = -sin;
m[1][1] = cos;
return this;
}
public Matrix4f initOrthographic(float left, float right, float bottom,
float top, float near, float far) {
initIdentity();
m[0][0] = 2.0f / (right - left);
m[1][1] = 2.0f / (top - bottom);
m[2][2] = 2.0f / (near - far);
m[0][3] = (left + right) / (left - right);
m[1][3] = (bottom + top) / (bottom - top);
m[2][3] = (near + far) / (far - near);
return this;
}
public Matrix4f mul(Matrix4f matrix) {
Matrix4f result = new Matrix4f();
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
float sum = 0.0f;
for (int index = 0; index < 4; index++)
sum += m[index][y] * matrix.get(x, index);
result.set(x, y, sum);
}
}
return result;
}
public float[][] getM() {
return m;
}
public float get(int x, int y) {
return m[x][y];
}
public void setM(float[][] m) {
this.m = m;
}
public void set(int x, int y, float value) {
m[x][y] = value;
}
public FloatBuffer toFloatBuffer() {
return Util.createFlippedBufferBuffer(this);
}
}
Shader class function
public void setUniform(String uniformName, Matrix4f matrix){
int uniformLocation = getUniformLocation(uniformName);
glUniformMatrix4(uniformLocation, false, matrix.toFloatBuffer());
}
Util class
package com.game.main.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import com.game.main.graphics.Vertex;
import com.game.main.maths.Matrix4f;
public class Util {
public static FloatBuffer createFlippedBuffer(float[] data){
FloatBuffer buffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
buffer.put(data).flip();
return buffer;
}
public static IntBuffer createFlippedBuffer(int[] data){
IntBuffer buffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
buffer.put(data).flip();
return buffer;
}
public static IntBuffer createIntBuffer(int size){
IntBuffer buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
return buffer;
}
public static FloatBuffer createFloatBuffer(int size){
FloatBuffer buffer = ByteBuffer.allocateDirect(size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
return buffer;
}
public static FloatBuffer createFlippedBufferBuffer(Matrix4f matrix){
FloatBuffer buffer = createFloatBuffer(4 * 4);
for(int x = 0; x < 4; x++)
for(int y = 0; y < 4; y++)
buffer.put(matrix.get(x, y));
buffer.flip();
return buffer;
}
public static FloatBuffer createFlippedBuffer(Vertex[] vertecies){
FloatBuffer buffer = createFloatBuffer(vertecies.length * Vertex.SIZE);
for(int i = 0; i < vertecies.length; i++){
buffer.put(vertecies[i].getPos().getX());
buffer.put(vertecies[i].getPos().getY());
buffer.put(vertecies[i].getPos().getZ());
buffer.put(vertecies[i].getTexCoord().getX());
buffer.put(vertecies[i].getTexCoord().getY());
}
buffer.flip();
return buffer;
}
}
Initializing the matrix uniform
Matrix4f matrix = new Matrix4f().initOrthographic(0, 800, 0, 600, -1.0f, 1.0f);
Shader.Object.setUniform("mat", matrix);
Vertex Shader
#version 330
layout(location = 0) in vec3 pos;
uniform mat4 mat;
void main(){
gl_Position = mat * vec4(pos, 1.0f);
}
Mesh class
package com.game.main.graphics;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import com.game.main.util.Util;
public class Mesh {
private int vbo, ibo, size;
public Mesh(Vertex[] vertecies, int[] indices){
vbo = glGenBuffers();
ibo = glGenBuffers();
size = indices.length;
createMesh(vertecies, indices);
}
private void createMesh(Vertex[] vertecies, int[] indices){
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, Util.createFlippedBuffer(vertecies), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * 4, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * 4, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Util.createFlippedBuffer(indices), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void bind(){
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
}
public void unbind(){
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
public void drawWithBinding()
{
bind();
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);
unbind();
}
public void draw(){
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);
}
public void destroy(){
glDeleteBuffers(ibo);
glDeleteBuffers(vbo);
}
}
Vertex class
package com.game.main.graphics;
import com.game.main.maths.Vector2f;
import com.game.main.maths.Vector3f;
public class Vertex {
private Vector3f pos = new Vector3f();
private Vector2f texCoord = new Vector2f();
public static final int SIZE = 5;
public Vertex(Vector3f pos){
this(pos, new Vector2f());
}
public Vertex(Vector3f pos, Vector2f texCoord){
this.pos = pos;
this.texCoord = texCoord;
}
public Vector3f getPos() {
return pos;
}
public void setPos(Vector3f pos) {
this.pos = pos;
}
public Vector2f getTexCoord() {
return texCoord;
}
public void setTexCoord(Vector2f texCoord) {
this.texCoord = texCoord;
}
}
transpose code
public Matrix4f transpose() {
float m00 = get(0, 0);
float m01 = get(1, 0);
float m02 = get(2, 0);
float m03 = get(3, 0);
float m10 = get(0, 1);
float m11 = get(1, 1);
float m12 = get(2, 1);
float m13 = get(3, 1);
float m20 = get(0, 2);
float m21 = get(1, 2);
float m22 = get(2, 2);
float m23 = get(3, 2);
float m30 = get(0, 3);
float m31 = get(1, 3);
float m32 = get(2, 3);
float m33 = get(3, 3);
set(0, 0, m00);
set(0, 1, m01);
set(0, 2, m02);
set(0, 3, m03);
set(1, 0, m10);
set(1, 1, m11);
set(1, 2, m12);
set(1, 3, m13);
set(2, 0, m20);
set(2, 1, m21);
set(2, 2, m22);
set(2, 3, m23);
set(3, 0, m30);
set(3, 1, m31);
set(3, 2, m32);
set(3, 3, m33);
return this;
}
Game class
package com.game.main;
import static org.lwjgl.opengl.GL11.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import com.game.main.graphics.Mesh;
import com.game.main.graphics.Shader;
import com.game.main.graphics.Vertex;
import com.game.main.maths.Matrix4f;
import com.game.main.maths.Vector3f;
public class Game{
private boolean running = false;
private Mesh mesh;
public Game(int width, int height){
try {
Display.setDisplayMode(new DisplayMode(800, 600));
Display.setTitle("2D Game");
Display.create();
} catch (LWJGLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Matrix4f matrix = new Matrix4f().initOrthographic(-1, 1, -1, 1, -1.0f, 1.0f);
Shader.Object.setUniform("mat", matrix);
Vertex[] vertecies = new Vertex[]{
new Vertex(new Vector3f(0.5f, 0.5f, 0)),
new Vertex(new Vector3f(0.5f, 0,0)),
new Vertex(new Vector3f(0, 0.5f,0)),
new Vertex(new Vector3f(0.5f, 0,0))
};
/*float[] vertecies = new float[]{
-10f, -10f * 9.0f / 16.0f, 1,
-10f, 10f * 9.0f / 16.0f,1,
0, 10f * 9.0f / 1.0f,1,
0, -10f * 9.0f / 6.0f,1
};*/
int[] ind = new int[]{
0, 1, 2,
2, 3, 0
};
mesh = new Mesh(vertecies, ind);
}
private void init(){
glEnable(GL_DEPTH_TEST);
}
public void start(){
if(running)
return;
running = true;
gameLoop();
}
public void stop(){
if(!running)
return;
running = false;
}
private void gameLoop(){
init();
long timer = System.currentTimeMillis();
long lastTime = System.nanoTime();
double delta = 0;
int frames = 0, updates = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / (1000000000.0 / 60.0);
lastTime = now;
while(delta >= 1){
if(Display.isCloseRequested())
stop();
update();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer >= 1000){
timer += 1000;
Display.setTitle("Frames: "+frames+" Updates: "+updates);
frames = updates = 0;
}
}
destroy();
}
private void update(){
}
private void render(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Shader.Object.bind();
mesh.drawWithBinding();
Shader.Object.unbind();
Display.update();
}
private void destroy(){
mesh.destroy();
Display.destroy();
}
}

It seems the problem is in setting the matrix uniform. OpenGL is a state machine and doesn't remember uniform values. More importantly, glUniformMatrix4 must be called while the shader is in use.
Currently, the matrix is set only at initialization (in the Game constructor), when it needs to be in render().
Shader.Object.bind(); //glUseProgram(programHandle)
Shader.Object.setUniform(...) //glUniformMatrix4
mesh.drawWithBinding(); //glDrawElements
Shader.Object.unbind(); //glUseProgram(0), but not necessary unless you want fixed function rendering
With mat not being set it'd probably be a zero matrix that was stopping things from drawing.
In addition to the above, there are two other potential issues:
Take a look in the bottom left of your screen and see if that pixel is on. I suspect everything's working, just really small. If the following works:
gl_Position = vec4(pos, 1.0f);
which is essentially a multiply by the identity matrix and nearly ortho(-1,1,-1,1,-1,1) (except the ortho call gives mat[2][2]=-1), then I'm guessing your mesh is around the size of a unit cube.
Introducing mat = initOrthographic(0, 800, 0, 600, -1.0f, 1.0f) looks like it's set up for a scene where vertices are given in pixels. The origin will be bottom left and if the mesh fits in a unit cube will draw to at most one pixel.
See #Jerem's answer about row/column major matrices.

It looks like your matrix is row major. You need to transpose it before sending it to the shader.
Note that there is a boolean parameter called transpose in glUniformMatrix4f but you can't use it in opengl es - the doc says it has to be false - you have to transpose it yourself.

Related

Transformation Hierarchy (Problem with modelling transformation of parent on children), OpenGL, LWJGL 3

I am currently building a scene graph in OpenGL and I trying to model parent transformations on their children, but it does not seem to be working. :(
My main problem is that the child's rotation does not follow the parent's rotation correctly. The child rotates around the parent object (center), which is what it is supposed to do, but the speed of it orbiting around the parent increases as the parent's rotation increase and slows back down once the parent does a full circle (when the parent's rotation reaches back to the rotation it started of at). Also I did have some problems with the correct translation of the child in relation to the parent, but I managed to temporarily fix them (I think). More info in the code. Sorry for the lengthy explanation, I would have definitely attached a video if I could, of the problem.
This is my transform class, where the transformations are set, particularly the getTransformation() method:
public class Transform {
private Vector3f position, lastPosition;
private Vector3f rotation, lastRotation;
private Vector3f scale, lastScale;
private Transform parent;
private Matrix4f parentMatrix;
public Transform() {
this(null, null, null);
}
public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
this.position = position != null ? position : new Vector3f(0, 0, 0);
this.rotation = rotation != null ? rotation : new Vector3f(0, 0, 0);
this.scale = scale != null ? scale : new Vector3f(1, 1, 1);
this.lastPosition = new Vector3f(0, 0, 0);
this.lastRotation = new Vector3f(0, 0, 0);
this.lastScale = new Vector3f(1, 1, 1);
parentMatrix = new Matrix4f().identity();
}
public boolean requireTMUpdate() { //checks if the matrix has changed and requires an update
if(parent != null) {
return parent.requireTMUpdate();
}
if(!position.equals(lastPosition)) {
lastPosition.set(position);
return true;
}
if(!rotation.equals(lastRotation)) {
lastRotation.set(rotation);
return true;
}
if(!scale.equals(lastScale)) {
lastScale.set(scale);
return true;
}
return false;
}
public Matrix4f getTransformation() {
if((parent != null) && (requireTMUpdate())) {
if(!(getParent().equals(getParent().getParent()))) {
parentMatrix.set(parent.getTransformation());
//The above line sets the updated parentMatrix
setPosition(parentMatrix.transformPosition(position));
//The above line sets the position to where child is supposed to be in
//relation the parent, otherwise once the key is stopped being pressed,
//it will return to the position at the start of the game / program.
}
}else {
parentMatrix.rotationXYZ(0, 0, 0).translation(0, 0, 0).scale(1);
// The above line is supposed to reset the matrix, otherwise the previous
//transformations or rotations add up each frame and the it just gets messed
//up.
}
//System.out.println(parentMatrix.toString());
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale))
// The transformationMatrix() method above from the Matrices class is
//supposed to return a worldMatrix.
}
public Vector3f getPosition() {
return position;
}
public void setPosition(Vector3f position) {
this.position = position;
}
public Vector3f getRotation() {
return rotation;
}
public void setRotation(Vector3f rotation) {
this.rotation = rotation;
}
public Vector3f getScale() {
return scale;
}
public void setScale(Vector3f scale) {
this.scale = scale;
}
public Transform getParent() {
return parent;
}
public void setParent(Transform parent) {
this.parent = parent;
}
}
Matrices class:
public class Matrices {
public static Matrix4f transformationMatrix(Vector3f pos, Vector3f rot, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f transformationMatrix(Vector3f pos, Quaternionf rotation, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
/*Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));*/
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, Vector3f rot) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, float pitch, float yaw, float roll) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(pitch)).
rotateY((float) Math.toRadians(yaw)).
rotateZ((float) Math.toRadians(roll));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f projectionMatrix(float FOV, float aspectRatio,
float NearPlaneDis, float FarPlaneDis) {
Matrix4f result = new Matrix4f();
result.identity();
result.perspective(FOV, aspectRatio, NearPlaneDis, FarPlaneDis);
/*float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FarPlaneDis - NearPlaneDis;
result.m00(x_scale);
result.m11(y_scale);
result.m22(-((FarPlaneDis + NearPlaneDis) / frustum_length));
result.m23(-1);
result.m32(-((2 * NearPlaneDis * FarPlaneDis) / frustum_length));
result.m33(0);*/
return result;
}
public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) {
if (dest == null)
dest = new Matrix4f();
dest.m30(dest.m30() + src.m00() * vec.x + src.m10() * vec.y + src.m20() * vec.z);
dest.m31(dest.m31() + src.m01() * vec.x + src.m11() * vec.y + src.m21() * vec.z);
dest.m32(dest.m32() + src.m02() * vec.x + src.m12() * vec.y + src.m22() * vec.z);
dest.m33(dest.m33() + src.m03() * vec.x + src.m13() * vec.y + src.m23() * vec.z);
return dest;
}
public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) {
if (dest == null)
dest = new Vector4f(0, 0, 0, 0);
float x = left.m00() * right.x + left.m10() * right.y + left.m20() * right.z + left.m30() * right.w;
float y = left.m01() * right.x + left.m11() * right.y + left.m21() * right.z + left.m31() * right.w;
float z = left.m02() * right.x + left.m12() * right.y + left.m22() * right.z + left.m32() * right.w;
float w = left.m03() * right.x + left.m13() * right.y + left.m23() * right.z + left.m33() * right.w;
dest.x = x;
dest.y = y;
dest.z = z;
dest.w = w;
return dest;
}
public static Vector3f scale(Vector3f vector, float scale) {
vector.x *= scale;
vector.y *= scale;
vector.z *= scale;
return vector;
}
public static float[] getAll(Matrix4f matrix) {
float[] f = new float[16];
return matrix.get(f);
}
public static float barryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
}
Finally, the Renderer class, where the objects are initialized:
public class Renderer {
private Model model;
private Model model2;
private GameObject root;
private GameObject child;
public Camera camera;
private float time = 0;
/*private float vertices[] = {
0.5f, 0.5f, 0f, //0 - top right
0.5f, -0.5f, 0f, //1 - bottom right
-0.5f, -0.5f, 0f, //2 - bottom left
-0.5f, 0.5f, 0f //3 - top left
};
private int indices[] = {
0, 1, 3, // first triangle
3, 1, 2 // second triangle
};*/
public Renderer() {
model = new Model("/backpack.obj");
model2 = new Model("/backpack.obj");
camera = new Camera(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
root = new GameObject();
child = new GameObject();
root.addComponent(camera);
root.addComponent(model);
child.addComponent(model2);
root.addChild(child);
model.getTransform().setPosition(new Vector3f(0, 0, 0));
model2.getTransform().setPosition(new Vector3f(10, 0, 10));
}
public void render(Shader shader) {
if(Input.isKeyDown(GLFW.GLFW_KEY_RIGHT)) {
model.getTransform().setRotation(new Vector3f(0, time += 1f, 0));
}
//model2.getTransform().setRotation(new Vector3f(0, time += 0.01f, 0));
shader.bind();
root.input();
root.update();
root.render(shader, camera);
shader.unbind();
}
public void cleanUp() {
for(Mesh mesh: model.getMeshes()) {
mesh.cleanUp();
}
}
}
What exactly am I doing wrong here? Any help is appreciated!
Nvm, I found the solution after a day's worth of debugging:
This line:
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale));
should actually be:
return new Matrix4f(parentMatrix).mul(Matrices.transformationMatrix(position, rotation, scale));.
Turns out the mul method in the JOML Matrix4f class does not return a new Matrix4f() class of the worldMatrix that should be outputted instead multiplies it to the current matrix (parentMatrix here) itself, creating a completely different parentMatrix for the next frame.

How to change specific part of vbo?

I have recently created 2D height map grid which generates 3D terrain mesh for my world With the ability to add hills/bumps with mouse click events during runtime. My Problem is that every time i add to the height of the vertices i update the whole terrain's normal and position vbos(very not efficient). what is the way to
change specific part of vbo?
I have heared that glBufferSubData is the way, but How can i change only the Y value? (the vbo is x,y,z,x,y,z...)
and get the changed verticies in order for glBufferSubData?
Terrain class:
public class Terrain {
public static final int SIZE = 500;
//VAO, vertexCount, VBOS
private RawModel model;
//textures for the terrain
private terrainTexturePack texturePack;
Loader loader;
private static int VERTEX_COUNT =128;
float[] Vertices;
float[] Normals;
float[] TextureCoords;
int[] Indices;
private float[][] heights;
public Terrain(Loader loader, terrainTexturePack texturePack) {
this.texturePack = texturePack;
this.loader = loader;
this.model = generateTerrain(loader);
}
public RawModel getModel() {
return model;
}
public terrainTexturePack getTexturePack() {
return texturePack;
}
//player collision detection witn the terrain
public Vector3f getXYZOfTerrain(float worldX, float worldZ) {
float gridSquareSize = SIZE / ((float) heights.length - 1);
int gridX = (int) Math.floor(worldX / gridSquareSize);
int gridZ = (int) Math.floor(worldZ / gridSquareSize);
if(gridX >= heights.length - 1 || gridZ >= heights.length - 1 || gridX < 0 || gridZ < 0) {
return null;
}
float xCoord = (worldX % gridSquareSize)/gridSquareSize;
float zCoord = (worldZ % gridSquareSize)/gridSquareSize;
float yCoord;
if (xCoord <= (1-zCoord)) {
yCoord = Maths.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ], 0), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
} else {
yCoord = Maths.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ + 1], 1), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
}
return new Vector3f(gridX, yCoord, gridZ);
}
//GENERATE THE TERRAIN
private RawModel generateTerrain(Loader loader) {
int pointer = 0;
int count = VERTEX_COUNT * VERTEX_COUNT;
heights = new float[VERTEX_COUNT][VERTEX_COUNT];
float[] vertices = new float[count * 3];
float[] normals = new float[count * 3];
float[] textureCoords = new float[count * 2];
int[] indices = new int[6 * (VERTEX_COUNT - 1) * (VERTEX_COUNT * 1)];
int vertexPointer = 0;
for (int i = 0; i < VERTEX_COUNT; i++) {
for (int j = 0; j < VERTEX_COUNT; j++) {
vertices[vertexPointer * 3] = (float) j / ((float) VERTEX_COUNT - 1) * SIZE;
float height = 0f;
vertices[vertexPointer * 3 + 1] = height;
heights[j][i] = height;
vertices[vertexPointer * 3 + 2] = (float) i / ((float) VERTEX_COUNT - 1) * SIZE;
Vector3f normal =new Vector3f(0, 1, 0);// calculateNormal(j, i, noise);
normals[vertexPointer * 3] = normal.x;
normals[vertexPointer * 3 + 1] = normal.y;
normals[vertexPointer * 3 + 2] = normal.z;
textureCoords[vertexPointer * 2] = (float) j / ((float) VERTEX_COUNT - 1);
textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) VERTEX_COUNT - 1);
vertexPointer++;
if(i < VERTEX_COUNT - 1 && j < VERTEX_COUNT - 1){
int topLeft = (i * VERTEX_COUNT) + j;
int topRight = topLeft + 1;
int bottomLeft = ((i + 1) * VERTEX_COUNT) + j;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
}
Vertices = vertices;
TextureCoords = textureCoords;
Normals = normals;
Indices = indices;
return loader.loadToVAO(vertices, textureCoords, normals, indices);
}
//Calculate normal
private Vector3f calculateNormal(int x, int z) {
float heightL = Vertices[((( (z) *VERTEX_COUNT)+ (x-1) )*3)+1];
float heightR = Vertices[((( (z) *VERTEX_COUNT)+ (x+1) )*3)+1];
float heightD = Vertices[((( (z-1) *VERTEX_COUNT)+ (x) )*3)+1];
float heightU = Vertices[((( (z+1) *VERTEX_COUNT)+ (x) )*3)+1];
Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
normal.normalise();
return normal;
}
//create mountain where the mouse clicked
//Vertices[(((y*VERTEX_COUNT)+x)*3)+1] = one Vertex in 2d grid
public void createHill(int x0, int y0){
float h = 0.06f;
int xs=VERTEX_COUNT;
int ys=VERTEX_COUNT;
float maxHeight =Vertices[(((y0*xs)+x0)*3)+1]+h;
float r = (9*maxHeight)/30;
//Loop the vertices
for(int y=(int) (y0-r);y<=y0+r;y++)
for(int x=(int) (x0-r);x<=x0+r;x++){
double circule = Math.sqrt((x-x0)*(x-x0)+(y0-y)*(y0-y));
if (circule <= r)
if ((x>=1)&&(x<xs-1))
if ((y>=1)&&(y<ys-1)){
Vertices[(((y*xs)+x)*3)+1] = Maths.hillsHeight(x0, x, y0, y,(maxHeight), r);
Vector3f normal = calculateNormal(x,y);
Normals[((((y*xs)+x))) * 3] = normal.x;
Normals[((((y*xs)+x))) * 3 + 1] = normal.y;
Normals[((((y*xs)+x))) * 3 + 2] = normal.z;
}
}
//change the whole VBO's not effective
//Note: i know that i dont need to update textures and indices
this.model=loader.loadToVAO(Vertices, TextureCoords, Normals, Indices);
}
}
Raw model class(vbo and vao holder):
//Store the VAOS and VBOS
public class RawModel {
private int vaoID;
private int vertexCount;
private int positionVbo;
private int normalVbo;
private int textureVbo;
public RawModel(int vaoID, int vertexCount, int positionVbo, int normalVbo, int textureVbo) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
this.positionVbo = positionVbo;
this.normalVbo = normalVbo;
this.textureVbo = textureVbo;
}
public RawModel(int vaoID, int vertexCount) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
public int getVaoID() {
return vaoID;
}
public int getVertexCount() {
return vertexCount;
}
public int getPositionVbo() {
return positionVbo;
}
public int getTextureVbo() {
return textureVbo;
}
public int getNormalVbo() {
return normalVbo;
}
}
loader class:
public class Loader {
//For clean up
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
private List<Integer> textures = new ArrayList<Integer>();
//Load mesh into VAO
public RawModel loadToVAO(float[] positions,float[] textureCoords,float[] normals,int[] indices){
int vaoID = createVAO();
bindIndicesBuffer(indices);
int positionvbo = storeDataInAttributeList(0,3,positions);
int textureVbo = storeDataInAttributeList(1,2,textureCoords);
int normalsnvbo = storeDataInAttributeList(2,3,normals);
unbindVAO();
return new RawModel(vaoID,indices.length, positionvbo, textureVbo, normalsnvbo);
}
//Load texture
public int loadTexture(String fileName) {
Texture texture = null;
try {
texture = TextureLoader.getTexture("PNG",
new FileInputStream("res/textures/" + fileName + ".png"));
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, -2);
if(GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
float amount = Math.min(4f,
GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT));
GL11.glTexParameterf(GL11.GL_TEXTURE_2D,
EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, amount);
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ".png , didn't work");
System.exit(-1);
}
textures.add(texture.getTextureID());
return texture.getTextureID();
}
//Clean up
public void cleanUp(){
for(int vao:vaos){
GL30.glDeleteVertexArrays(vao);
}
for(int vbo:vbos){
GL15.glDeleteBuffers(vbo);
}
for(int texture:textures){
GL11.glDeleteTextures(texture);
}
}
//Creates vao
private int createVAO(){
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
//Store data in vbo
private int storeDataInAttributeList(int attributeNumber, int coordinateSize,float[] data){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber,coordinateSize,GL11.GL_FLOAT,false,0,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
return vboID;
}
private void unbindVAO(){
GL30.glBindVertexArray(0);
}
//Bind indices buffer
private void bindIndicesBuffer(int[] indices){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
IntBuffer buffer = storeDataInIntBuffer(indices);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
}
//Store in int buffer
private IntBuffer storeDataInIntBuffer(int[] data){
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Store in float buffer
private FloatBuffer storeDataInFloatBuffer(float[] data){
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Load skyBox textures
public int loadCubeMap(String[] textureFiles){
int texID = GL11.glGenTextures();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);
for(int i = 0; i < textureFiles.length; i++){
TextureData data = decodeTextureFile("res/textures/"+ textureFiles[i] + ".png");
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getBuffer());
}
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
textures.add(texID);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
return texID;
}
private TextureData decodeTextureFile(String fileName) {
int width = 0;
int height = 0;
ByteBuffer buffer = null;
try {
FileInputStream in = new FileInputStream(fileName);
PNGDecoder decoder = new PNGDecoder(in);
width = decoder.getWidth();
height = decoder.getHeight();
buffer = ByteBuffer.allocateDirect(4 * width * height);
decoder.decode(buffer, width * 4, Format.RGBA);
buffer.flip();
in.close();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ", didn't work");
System.exit(-1);
}
return new TextureData(buffer, width, height);
}
//Load textures for GUI
public RawModel loadToVAO(float[] positions, int dimensions) {
int vaoID = createVAO();
this.storeDataInAttributeList(0, dimensions, positions);
unbindVAO();
return new RawModel(vaoID, positions.length / dimensions);
}
}
Solved Thank to Reto Koradi
public void changeVbo(int position, float[] data, int VboId){
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VboId);
FloatBuffer ArrayData = storeDataInFloatBuffer(data);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER,position * 4, ArrayData);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
The easiest and likely most efficient way is to store the heights (y-values) in a separate VBO, and specify them as a separate vertex attribute.
Then, in your vertex shader code, you can simply reassemble the position from the separate attributes. You might have something like this in your shader code now:
in vec3 pos;
This changes to:
in vec3 posXZ;
in float posY;
...
vec3 pos = vec3(posXZ.x, posY, posXZ.y);
Using a separate VBO for data that changes frequently also allows you to specify the allocation flags accordingly. You can use GL_DYNAMIC_DRAW for the data that changes frequently, GL_STATIC_DRAW for the rest.
Another option would be to use glMapBuffer(). This gives you a CPU pointer to the buffer content, allowing you to modify only the data that you actually want to change. However, you'll have to be careful that you don't introduce undesirable synchronization between CPU and GPU. The glMapBuffer() call might block until the GPU finished all rendering calls using the previous content of the buffer. One common technique is to use multiple copies of the data in a set of buffers, and cycle through them, to minimize synchronization. But if the amount of data is large, that will obviously cause memory usage to increase dramatically.
In your use case, I suspect that you'll have to update the normals as well, since they depend on the height values.

How to apply color to an irregular polygon with unknown indices?

I'm having a set of vertices (X,Y) of an irregular polygon, some having 4 vertices and some more than 4. I'm creating an array of vertices for drawing from the set of vertices I have using the getTransformedVertices() method. The indices and UV for the texture are unknown so I had to calculate tem using Triangulate() method below. I've setup the color and texture in the setColor() and setImage() methods.
However, the polygons having more than 4 vertices are not rendered properly. I've been trying for weeks now and almost the searched half of the internet. This is what I could come up with. The polygons with 4 vertices and the outlines are working properly. But I couldn't get the textures displayed properly on the polygon. Please help
public class BoothRectangle
{
float angle;
float scale;
RectF base;
PointF translation;
int textureId;
int positionBufferId;
int textureBufferId;
PointF[] corners;
float[] verts;
// Geometric variables
public float vertices[];
public float colors[];
public short indices[];
public float uvs[];
public FloatBuffer vertexBuffer;
public ShortBuffer drawListBuffer;
public FloatBuffer colorBuffer;
public FloatBuffer uvBuffer;
TextPaint textPaint = new TextPaint();
String title;
public BoothRectangle(PointF[] corners, int textureId, float[] colors, String title)
{
// Initialise our intital size around the 0,0 point
base = new RectF(corners[1].x, corners[3].y, corners[0].x, corners[1].y);
this.corners = corners;
this.title = title;
// Offset translation
translation = new PointF(0f,0f);
// Initial Size
scale = 1f;
// We start in our inital angle
angle = 0f;
this.textureId = textureId;
this.colors = colors;
}
public void setColor(float[] topColor){
List<Float> colorsList = new ArrayList<Float>();
for(PointF point : corners){
colorsList.add(topColor[0] / 255);
colorsList.add(topColor[1] / 255);
colorsList.add(topColor[2] / 255);
colorsList.add(1f);
}
int i = 0;
float[] colors = new float[colorsList.size()];
for (Float f : colorsList) {
colors[i++] = (f != null ? f : Float.NaN);
}
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * colors.length, colorBuffer, GLES11.GL_STATIC_DRAW);
textureBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
}
public float[] getTransformedVertices()
{
float z;
List<Float> finalVertices = new ArrayList<Float>();
if(textureId == 0)
z = 0.5f;
else
z = 2.0f;
finalVertices.clear();
for(PointF point : corners){
finalVertices.add(point.x);
finalVertices.add(point.y);
finalVertices.add(0.0f);
}
int i = 0;
float[] verticesArray = new float[finalVertices.size()];
for (Float f : finalVertices) {
verticesArray[i++] = (f != null ? f : Float.NaN);
}
return verticesArray;
}
public void setImage()
{
uvs = new float[] {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * uvs.length, uvBuffer, GLES11.GL_STATIC_DRAW);
textureBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
}
public void initBooth()
{
vertices = this.getTransformedVertices();
indices = PolygonTriangulation.Process(vertices);
if(indices==null){
Log.d("PolygonTriangulation",title + " - failed");
if(this.corners.length == 4){
indices = new short[] {2, 1, 0, 2, 0, 3};
}else{
indices = new short[corners.length];
for(int i=0;i<corners.length;i++){
indices[i] = (short) i;
}
}
}
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * vertices.length, vertexBuffer, GLES11.GL_STATIC_DRAW);
positionBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
if(this.textureId !=0){
setImage();
} else {
setColor(colors);
}
}
public void Render(GL10 gl){
if(textureId == 0){
GLES11.glPushMatrix();
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, positionBufferId);
GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glFrontFace(GL10.GL_CW);
GLES11.glColor4f(0.8f, 0.8f, 0.8f, 0.8f);
GLES11.glLineWidth(3.0f);
GLES11.glDrawArrays(GL10.GL_LINE_LOOP, 0, corners.length);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, textureBufferId);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
GLES11.glColorPointer(4, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, drawListBuffer);
GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glDisableClientState(GL10.GL_COLOR_ARRAY);
GLES11.glPopMatrix();
} else {
GLES11.glPushMatrix();
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, positionBufferId);
GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, textureBufferId);
GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glEnable(GL10.GL_TEXTURE_2D);
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
GLES11.glEnable(GL10.GL_BLEND);
GLES11.glFrontFace(GL10.GL_CW);
GLES11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR)
{
Log.e("OPENGL", "GL Texture Load Error: " + GLU.gluErrorString(error));
}
GLES11.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, drawListBuffer);
GLES11.glDisable(GL10.GL_BLEND);
GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLES11.glDisable(GL10.GL_TEXTURE_2D);
GLES11.glPopMatrix();
}
}
}
PolygonTriangulation.java - got from AssetFX - http://www.experts-exchange.com/Programming/Languages/Java/Q_27882746.html
public class PolygonTriangulation {
static final float EPSILON=0.0000000001f;
static public float Area(float[] contour) {
int n = contour.length;
float A = 0.0f;
for(int p = n - 3, q = 0; q < n; p=q, q+=3)
{
A += contour[p] * contour[q+1] - contour[q] * contour[p+1];
}
return A * 0.5f;
}
static public boolean InsideTriangle(float Ax,float Ay,float Bx,float By,float Cx,float Cy,float Px,float Py){
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
float cCROSSap, bCROSScp, aCROSSbp;
ax = Cx - Bx; ay = Cy - By;
bx = Ax - Cx; by = Ay - Cy;
cx = Bx - Ax; cy = By - Ay;
apx= Px - Ax; apy= Py - Ay;
bpx= Px - Bx; bpy= Py - By;
cpx= Px - Cx; cpy= Py - Cy;
aCROSSbp = ax * bpy - ay * bpx;
cCROSSap = cx * apy - cy * apx;
bCROSScp = bx * cpy - by * cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}
static public boolean Snip(float[] contour, int u, int v, int w, int n, int[] V) {
int p;
float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
Ax = contour[V[u]];
Ay = contour[V[u+1]];
Bx = contour[V[v]];
By = contour[V[v+1]];
Cx = contour[V[w]];
Cy = contour[V[w+1]];
if ( EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) ){
return false;
}
for (p = 0; p < n; p++)
{
if( (p == u/2) || (p == v/2) || (p == w/2) ){
continue;
}
Px = contour[V[p*2]];
Py = contour[V[(p*2)+1]];
if (InsideTriangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py)){
return false;
}
}
return true;
}
//Brings in 3D vertex float array but processes it as 2D only.
static public short[] Process(float[] contour) {
//Pre-return list
ArrayList<Integer> vertexArray = new ArrayList<Integer>();
/* allocate and initialize list of Vertices in polygon */
int n = contour.length;
if ( n/3 < 3 )
return null;
//The (n/3)*2 occurs as we are removing the z coordinate from the mix
int[] V = new int[(n/3)*2];
/* we want a counter-clockwise polygon in V */
if (0.0f < Area(contour)){
for (int s = 0, v = 0; v < n-1; s+=2, v += 3){
V[s] = v;
V[s + 1] = v + 1;
}
}
else{
for(int s = 0, v = 0; v < n-1; s += 2, v += 3){
V[s] = (n - 1) - (v + 2);
V[s + 1] = (n - 1) - (v + 1);
}
}
int nv = n/3;
/* remove nv-2 Vertices, creating 1 triangle every time */
int count = 2 * nv; /* error detection */
for(int v = nv - 1; nv > 2;)
{
/* if we loop, it is probably a non-simple polygon */
if (0 >= (count--))
{
//** Triangulate: ERROR - probable bad polygon!
Log.e("PolygonTriangulation","Invalid Polygon");
return null;
}
/* three consecutive vertices in current polygon, <u,v,w> */
int u = v;
if (nv <= u)
u = 0; /* previous */
v = u + 1;
if (nv <= v)
v = 0; /* new v */
int w = v + 1;
if (nv <= w)
w = 0; /* next */
if (Snip(contour, u*2, v*2, w*2, nv, V))
{
vertexArray.add(V[u*2]/3);
vertexArray.add(V[v*2]/3);
vertexArray.add(V[w*2]/3);
// remove v from remaining polygon
for(int s = v * 2, t = (v * 2) + 2; t < (nv * 2); s += 2, t += 2){
V[s] = V[t];
V[s+1] = V[t+1];
}
nv--;
// reset error detection counter
count = 2 * nv;
}
}
//Convert ArrayList into short array
short[] index = new short[vertexArray.size()];
for(int i = 0; i < vertexArray.size(); i++){
index[i] = vertexArray.get(i).shortValue();
}
return index;
}
}

Java - Use the vertex buffer object (vbo) LWJGL

OK, I have the following code, i am trying to convert this code into the vertex buffer object render way. but it doesn't seem to work..
So here is the code:
package com.game.base;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.util.glu.GLU.gluPerspective;
import java.nio.FloatBuffer;
import java.util.Random;
import org.lwjgl.BufferUtils;
import org.lwjgl.input.Keyboard;
import com.game.display.graphics.Entity;
public class Game {
float speed = 0.0f;
float speedChange = 0.000001f;
int stars = 20000;
int starsDistance = 4000;
float starSize = 2.0f;
int vertices = 3;
int vertex_size = 3; // X, Y, Z,
int color_size = 3; // R, G, B,
Entity[] points;
Random random = new Random();
public Game() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective((float) 60, (float) (MainClass.WIDTH / MainClass.HEIGHT), 0.001f, 100);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
points = new Entity[stars];
for (int i = 0; i < points.length; i++) {
points[i] = new Entity((random.nextFloat() - 0.5f) * 100f, (random.nextFloat() - 0.5f) * 100f, random.nextInt(starsDistance) - starsDistance);
}
}
public void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0, 0, speed);
glPointSize(2.0f);
glBegin(GL_POINTS);
for (Entity p : points) {
glVertex3f(p.x, p.y, p.z);
}
glEnd();
}
public void update() {
}
public void dispose() {
}
public void input() {
if (Keyboard.isKeyDown(Keyboard.KEY_UP)) {
speed += speed < 0 ? speedChange * 5 : speedChange;
}
if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) {
speed -= speed > 0 ? speedChange * 5 : speedChange;
}
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
speed = 0f;
}
if (Keyboard.isKeyDown(Keyboard.KEY_C)) {
speed = 0;
glLoadIdentity();
}
if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
MainClass.stop();
}
}
}
Any ideas how to convert this into VBO (Vertex Buffer Object) ?
Can someone convert this into VBO and show me how?
Thanks in Advance,
And I accept fast :)

Android OpenGLES 2 ray picking from touch coordinates, unprojecting calculation slightly off

I am trying to implement object picking based on touch coordinates via an intersecting ray test.
I am having trouble finding information on converting the touch coordinates to the coordinate system used in the world in order to construct this ray.
My understanding so far is that the matrix that is applied to each vertex in the scene is:
projectionMatrix * viewMatrix * modelMatrix
Here is my process for reversing that process in a an attempt to find the ray's endpoint in the scene as well as my drawing loop in case I'm simply applying the different matrices incorrectly:
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] modelView, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2 * touchX/windowWidth - 1;
float normalizedY = 1 - 2*touchY/windowHeight;
float[] unviewMatrix = new float[16];
float[] viewMatrix = new float[16];
Matrix.multiplyMM(viewMatrix, 0, projection, 0, modelView, 0);
Matrix.invertM(unviewMatrix, 0, viewMatrix, 0);
float[] nearPoint = multiplyMat4ByVec4(projection, new float[]{normalizedX, normalizedY, 0, 1});
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
rayDirection[0] = nearPoint[0] - cameraPos[0];
rayDirection[1] = nearPoint[1] - cameraPos[1];
rayDirection[2] = nearPoint[2] - cameraPos[2];
rayDirection[3] = nearPoint[3] - cameraPos[3];
return rayDirection;
}
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
#Override
public void onDrawFrame(GL10 gl10) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
float angleInDegrees = (360.0f / 10000.0f) * ((int) time);
GLES20.glViewport(0, 0, (int)(width/2), (int)(height/2));
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 1.5f, 0f, 0f, -5f, 0f, 1f, 0f);
//Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
drawTriangle(triangleVertices);
//Matrix.translateM(mModelMatrix, 0, 1.5f, 0, -1f);
//Matrix.frustumM(mProjectionMatrix, 0, left, right, -1.0f, 1.0f, 1.0f, 10.0f);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, 1.5f, 0.8f, 0.5f, 0f, 0f, 0f, 0f, 1f, 0f);
GLES20.glViewport((int)(width/2), (int)(height/2), (int)(width/2), (int)(height/2));
drawTriangle(triangleVertices);
drawIntersectionLine();
/*
Matrix.setLookAtM(viewMatrix, 0, 0, 1.5f, 0.5f, 0, 0, 0, 0, 0, -1f);
GLES20.glViewport((int)(width/2), (int)height, (int)(width/2), (int)(height/2));
drawTriangle(triangleVertices);
drawIntersectionLine();
*/
}
private void drawTriangle(final FloatBuffer triangleBuffer)
{
triangleBuffer.position(positionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
triangleBuffer.position(colorOffset);
GLES20.glVertexAttribPointer(mColorHandle, colorDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
Matrix.multiplyMM(mMVPMatrix, 0, viewMatrix, 0, mModelMatrix, 0);
mMVMatrix = mMVPMatrix;
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
//Log.d("OpenGLES2Test", "The intersection ray is: " + floatArrayAsString(getCameraPos(mMVMatrix)) + " + " + floatArrayAsString(getMouseRayProjection((int)(width / 2), (int)(height / 2), 1.0f, (int)width, (int)height, mMVMatrix, mProjectionMatrix)));
}
private void drawIntersectionLine()
{
lineVertices.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, lineStrideBytes, lineVertices);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2);
}
private void moveIntersectionLineEndPoint(float[] lineEndPoint)
{
this.lineEndPoint = lineEndPoint;
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
Although I'm pretty sure my 4x4 matrix by 4d vector multiplication method is correct, here is that method as well just in case:
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
The goal of this test app is to show the scene from a few separate angles so that I can see how the intersection line looks based on my code. I wanted to draw the line starting at the camera's origin and ending at the intersection point, but it's acting oddly. The endpoint seems to be being pushed farther along the x axis in the positive direction than it should be, and in some spots it's seems to sort of...skip, as if there were a hole at that location or something. Although I still remember a bit of linear algebra from calculus, I don't remember enough to know exactly what I'm doing here and I've scoured through many of the resources online with no luck. I'm hoping someone that reads this will have more experience dealing with this than I and will be kind enough to help me, or give me any tips if there's something else that I may be doing the wrong or in an inefficient way.
Variable Reference:
Matrices are all float arrays of length 16
mProjectionMatrix = projection matrix
mModelMatrix = model matrix
mMVPMatrix = projection * modelview matrix
mMVMatrix = modelview matrix
private final FloatBuffer triangleVertices;
private FloatBuffer lineVertices;
private final int bytesPerFloat = 4;
private float[] viewMatrix = new float[16];
private static Context context;
private int mMVPMatrixHandle;
private int mPositionHandle;
private int mColorHandle;
private float[] mProjectionMatrix = new float[16];
private float[] mModelMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
private float[] mMVMatrix = new float[16];
private final int strideBytes = 7 * bytesPerFloat;
private final int lineStrideBytes = 3 * bytesPerFloat;
private final int positionOffset = 0;
private final int positionDataSize = 3;
private final int colorOffset = 3;
private final int colorDataSize = 4;
private float width, height;
private float[] lineStartPoint = new float[]{0, 0, 1.5f};
private float[] lineEndPoint = new float[]{0, 0, 0};
After some searching, I found a page that details this process in a different manner. Now I no longer have the issue with the end of the ray jumping to an unexpected position at random times and the end point points to the exact location it should!
Here is the page I used to fix my process:
http://www.antongerdelan.net/opengl/raycasting.html
And here is my final source code for the entire intersection testing app. Most of the relevant code is within the OpenGLRenderer class under the getMouseRayProjection method.
MainActivity.java:
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.MotionEvent;
public class MainActivity extends Activity {
private MyGLSurfaceView mGLSurfaceView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new MyGLSurfaceView(this);
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
OpenGLRenderer renderer = new OpenGLRenderer(this);
mGLSurfaceView.setRenderer(renderer);
mGLSurfaceView.renderer = renderer;
setContentView(mGLSurfaceView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
#Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
}
class MyGLSurfaceView extends GLSurfaceView {
public OpenGLRenderer renderer;
public float previousX, previousY;
public MyGLSurfaceView(Context context)
{
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent e)
{
float x = e.getX();
float y = e.getY();
switch(e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - previousX;
float dy = y - previousY;
renderer.onTouch(x, y);
}
previousX = x;
previousY = y;
return true;
}
}
OpenGLRenderer.java:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLU;
import android.opengl.Matrix;
import android.opengl.GLSurfaceView;
import android.os.SystemClock;
import android.util.Log;
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private final FloatBuffer triangleVertices;
private FloatBuffer lineVertices;
private final int bytesPerFloat = 4;
private float[] viewMatrix = new float[16];
private static Context context;
private int mMVPMatrixHandle;
private int mPositionHandle;
private int mColorHandle;
private float[] mProjectionMatrix = new float[16];
private float[] mModelMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
private float[] mMVMatrix = new float[16];
private int[] viewport = new int[4];
private final int strideBytes = 7 * bytesPerFloat;
private final int lineStrideBytes = 3 * bytesPerFloat;
private final int positionOffset = 0;
private final int positionDataSize = 3;
private final int colorOffset = 3;
private final int colorDataSize = 4;
private float width, height;
private float[] lineStartPoint = new float[]{0, 0, 1f};
private float[] lineEndPoint = new float[]{0, 0, 0};
private float[] cameraPos = new float[]{0f, 0f, 2.5f};
private float[] cameraLook = new float[]{0f, 0f, -5f};
private float[] cameraUp = new float[]{0f, 1f, 0f};
public OpenGLRenderer(Context context) {
this.context = context;
final float[] triangleVerticesData = {
-0.5f, -0.25f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.25f, 0.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.559016994f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f
};
triangleVertices = ByteBuffer.allocateDirect(triangleVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
triangleVertices.put(triangleVerticesData).position(0);
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
#Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
Matrix.setLookAtM(viewMatrix, 0, cameraPos[0], cameraPos[1], cameraPos[2], cameraLook[0], cameraLook[1], cameraLook[2], cameraUp[0], cameraUp[1], cameraUp[2]);
try {
int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
if (vertexShaderHandle != 0)
{
GLES20.glShaderSource(vertexShaderHandle, readShader("vertexShader"));
GLES20.glCompileShader(vertexShaderHandle);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
}
}
if (vertexShaderHandle == 0)
{
throw new RuntimeException("Error creating vertex shader");
}
int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if (fragmentShaderHandle != 0)
{
GLES20.glShaderSource(fragmentShaderHandle, readShader("fragmentShader"));
GLES20.glCompileShader(fragmentShaderHandle);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0)
{
GLES20.glDeleteShader(fragmentShaderHandle);
fragmentShaderHandle = 0;
}
}
if (fragmentShaderHandle == 0)
{
throw new RuntimeException("Error creating fragment shader.");
}
int programHandle = GLES20.glCreateProgram();
if (programHandle != 0)
{
GLES20.glAttachShader(programHandle, vertexShaderHandle);
GLES20.glAttachShader(programHandle, fragmentShaderHandle);
GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
GLES20.glBindAttribLocation(programHandle, 1, "a_Color");
GLES20.glLinkProgram(programHandle);
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0)
{
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0)
{
throw new RuntimeException("Error creating program.");
}
mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");
mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color");
GLES20.glUseProgram(programHandle);
} catch (IOException e)
{
Log.d("OpenGLES2Test", "The shader could not be read: " + e.getMessage());
} catch (RuntimeException e)
{
Log.d("OpenGLES2Test", e.getMessage());
}
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glDepthMask(true);
}
#Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width/2, height/2);
this.width = width;
this.height = height;
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 1.0f;
final float far = 10.0f;
GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, viewport, 0);
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
#Override
public void onDrawFrame(GL10 gl10) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
GLES20.glViewport(0, 0, (int)(width), (int)(height));
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.setLookAtM(viewMatrix, 0, cameraPos[0], cameraPos[1], cameraPos[2], cameraLook[0], cameraLook[1], cameraLook[2], cameraUp[0], cameraUp[1], cameraUp[2]);
Matrix.multiplyMM(mMVMatrix, 0, viewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
drawTriangle(triangleVertices);
drawIntersectionLine();
}
private void drawTriangle(final FloatBuffer triangleBuffer)
{
triangleBuffer.position(positionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
triangleBuffer.position(colorOffset);
GLES20.glVertexAttribPointer(mColorHandle, colorDataSize, GLES20.GL_FLOAT, false, strideBytes, triangleBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
private void drawIntersectionLine()
{
lineVertices.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, positionDataSize, GLES20.GL_FLOAT, false, lineStrideBytes, lineVertices);
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2);
}
private void moveIntersectionLineEndPoint(float[] lineEndPoint)
{
this.lineEndPoint = lineEndPoint;
float[] lineVerticesData = {
lineStartPoint[0], lineStartPoint[1], lineStartPoint[2],
lineEndPoint[0], lineEndPoint[1], lineEndPoint[2]
};
lineVertices = ByteBuffer.allocateDirect(lineVerticesData.length * bytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
lineVertices.put(lineVerticesData).position(0);
}
public static String readShader(String filePath) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(context.getAssets().open(filePath)));
StringBuilder sb = new StringBuilder();
String line;
while( ( line = reader.readLine() ) != null)
{
sb.append(line + "\n");
}
reader.close();
return sb.toString();
}
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] view, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2f * touchX/windowWidth - 1f;
float normalizedY = 1f - 2f*touchY/windowHeight;
float normalizedZ = 1.0f;
float[] rayNDC = new float[]{normalizedX, normalizedY, normalizedZ};
float[] rayClip = new float[]{rayNDC[0], rayNDC[1], -1f, 1f};
float[] inverseProjection = new float[16];
Matrix.invertM(inverseProjection, 0, projection, 0);
float[] rayEye = multiplyMat4ByVec4(inverseProjection, rayClip);
rayClip = new float[]{rayClip[0], rayClip[1], -1f, 0f};
float[] inverseView = new float[16];
Matrix.invertM(inverseView, 0, view, 0);
float[] rayWorld4D = multiplyMat4ByVec4(inverseView, rayEye);
float[] rayWorld = new float[]{rayWorld4D[0], rayWorld4D[1], rayWorld4D[2]};
rayDirection = normalizeVector3(rayWorld);
return rayDirection;
}
public float[] normalizeVector3(float[] vector3)
{
float[] normalizedVector = new float[3];
float magnitude = (float) Math.sqrt((vector3[0] * vector3[0]) + (vector3[1] * vector3[1]) + (vector3[2] * vector3[2]));
normalizedVector[0] = vector3[0] / magnitude;
normalizedVector[1] = vector3[1] / magnitude;
normalizedVector[2] = vector3[2] / magnitude;
return normalizedVector;
}
/*
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] modelView, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2 * touchX/windowWidth - 1;
float normalizedY = 1 - 2*touchY/windowHeight;
float[] unviewMatrix = new float[16];
float[] viewMatrix = new float[16];
Matrix.multiplyMM(viewMatrix, 0, projection, 0, modelView, 0);
Matrix.invertM(unviewMatrix, 0, viewMatrix, 0);
float[] nearPoint = multiplyMat4ByVec4(unviewMatrix, new float[]{normalizedX, normalizedY, 0, 1});
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
rayDirection[0] = (nearPoint[0] - cameraPos[0]);
rayDirection[1] = (nearPoint[1] - cameraPos[1]);
rayDirection[2] = (nearPoint[2] - cameraPos[2]);
rayDirection[3] = (nearPoint[3] - cameraPos[3]);
return rayDirection;
}
*/
/*
public float[] getOGLPosition(int x, int y)
{
GLU.gluUnProject(x, y, 0, , modelOffset, project, projectOffset, view, viewOffset, obj, objOffset)
}
*/
public float[] getCameraPos(float[] modelView)
{
float[] modelviewInverse = new float[16];
Matrix.invertM(modelviewInverse, 0, modelView, 0);
float[] cameraPos = new float[4];
cameraPos[0] = modelviewInverse[12];
cameraPos[1] = modelviewInverse[13];
cameraPos[2] = modelviewInverse[14];
cameraPos[3] = modelviewInverse[15];
return cameraPos;
}
public String floatArrayAsString(float[] array)
{
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Float f : array)
{
sb.append(f + ", ");
}
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
public float[] getInverseMatrix(float[] originalMatrix)
{
float[] inverseMatrix = new float[16];
Matrix.invertM(inverseMatrix, 0, originalMatrix, 0);
return inverseMatrix;
}
public float[] multiplyMat4ByVec4(float[] matrix4, float[] vector4)
{
float[] returnMatrix = new float[4];
returnMatrix[0] = (matrix4[0] * vector4[0]) + (matrix4[1] * vector4[1]) + (matrix4[2] * vector4[2]) + (matrix4[3] * vector4[3]);
returnMatrix[1] = (matrix4[4] * vector4[0]) + (matrix4[5] * vector4[1]) + (matrix4[6] * vector4[2]) + (matrix4[7] * vector4[3]);
returnMatrix[2] = (matrix4[8] * vector4[0]) + (matrix4[9] * vector4[1]) + (matrix4[10] * vector4[2]) + (matrix4[11] * vector4[3]);
returnMatrix[3] = (matrix4[12] * vector4[0]) + (matrix4[13] * vector4[1]) + (matrix4[14] * vector4[2]) + (matrix4[15] * vector4[3]);
return returnMatrix;
}
public void onTouch(float touchX, float touchY)
{
float[] mouseRayProjection = getMouseRayProjection(touchX, touchY, width, height, mMVMatrix, mProjectionMatrix);
Log.d("OpenGLES2Test", "Mouse Ray: " + floatArrayAsString(mouseRayProjection));
//Log.d("OpenGLES2Test", "ModelView: " + floatArrayAsString(mMVMatrix));
//Log.d("OpenGLES2Test", "ModelViewInverse: " + floatArrayAsString(getInverseMatrix(mMVMatrix)));
//Log.d("OpenGLES2Test", "Mouse Coordinates: " + touchX + ", " + touchY);
//Log.d("OpenGLES2Test", "Ray Coordinates: " + mouseRayProjection[0] + ", " + mouseRayProjection[1] + ", " + mouseRayProjection[2] + ", " + mouseRayProjection[3]);
moveIntersectionLineEndPoint(mouseRayProjection);
}
}
fragmentShader:
precision mediump float;
varying vec4 v_Color;
void main()
{
gl_FragColor = v_Color;
}
vertexShader:
uniform mat4 u_MVPMatrix;
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main()
{
v_Color = a_Color;
gl_Position = u_MVPMatrix * a_Position;
}
your codes are good but it does not work fine for me. After studying the code from Rajawali ( https://github.com/MasDennis/Rajawali ), I would suggest the following changes in your final codes:
public float[] getMouseRayProjection(float touchX, float touchY, float windowWidth, float windowHeight, float[] view, float[] projection)
{
float[] rayDirection = new float[4];
float normalizedX = 2f * touchX/windowWidth - 1f;
float normalizedY = 1f - 2f*touchY/windowHeight;
float normalizedZ = 1.0f;
float[] rayClip1 = new float[]{normalizedX, normalizedY, -1, 1};
float[] rayClip2 = new float[]{normalizedX, normalizedY, 1, 1};
float[] mVPMatrix = new float[16];
float[] invertVPMatrix = new float[16];
Matrix.multiplyMM(mVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.invertM(invertVPMatrix, 0, mVPMatrix, 0);
float[] rayWorld1 = new float[4];
Matrix.multiplyMV(rayWorld1, 0, invertVPMatrix, 0, rayClip1, 0);
float[] rayWorld2 = new float[4];
Matrix.multiplyMV(rayWorld2, 0, invertVPMatrix, 0, rayClip2, 0);
if (rayWorld1[3]!=0 && rayWorld2[3]!=0)
{
rayWorld1[0] = rayWorld1[0] / rayWorld1[3];
rayWorld1[1] = rayWorld1[1] / rayWorld1[3];
rayWorld1[2] = rayWorld1[2] / rayWorld1[3];
rayWorld1[3] = 1;
rayWorld2[0] = rayWorld2[0] / rayWorld2[3];
rayWorld2[1] = rayWorld2[1] / rayWorld2[3];
rayWorld2[2] = rayWorld2[2] / rayWorld2[3];
rayWorld2[3] = 1;
}
.... continue determine which object has intersection with the ray.
I think the most important part is that you should divide the vector by the W element.

Categories