In an application, while trying to rotate an object using touch, I noticed drift in position of object after sometime (without any translation applied !!). The rotation is only about z-axis and works perfectly, but drift happens only after few rotations.
ds will be used for translation (using up-down button).
_uNozzleCentreMatrix and _ModelMatrixNozzle will use ds if I correct this.
private static final float[] _uNozzleCentre = new float[]{0.0f, 0.333605f, 0.0f, 1.0f};
protected static float[] _uNozzleCentreMatrix = new float[4];
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.setLookAtM(GLES20Renderer._ViewMatrix, 0, 0, 0, 7f, 0, 0, 0, 0, 1, 0);
Matrix.frustumM(GLES20Renderer._ProjectionMatrix, 0, -ratio, ratio, -1, 1, 2, 8);
Matrix.setIdentityM(GLES20Renderer._ModelMatrixNozzle, 0);
}
private static void updateModel(int upDown, float xAngle, float yAngle, float zAngle) {
//ds = GLES20Renderer._upDown - GLES20Renderer._lastUpDown;
ds = 0; // ds changes with button up-down, but now it is made 0, so button up-down will not affect it
Matrix.multiplyMV(GLES20Renderer._uNozzleCentreMatrix, 0, GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentre, 0);
if(Math.abs(ds) > 0) {
} else {
if(GLES20Renderer._zAngle >= 360) {
GLES20Renderer._zAngle = GLES20Renderer._zAngle - 360;
}
if(GLES20Renderer._zAngle <= -360) {
GLES20Renderer._zAngle = GLES20Renderer._zAngle + 360;
}
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentreMatrix[0], GLES20Renderer._uNozzleCentreMatrix[1], 0);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._zAngle, 0, 0, 1);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._lastZAngle, 0, 0, 1);
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._uNozzleCentreMatrix[0], -GLES20Renderer._uNozzleCentreMatrix[1], 0);
}
Matrix.multiplyMM(GLES20Renderer._MVPMatrixNozzle, 0, GLES20Renderer._ViewMatrix, 0, GLES20Renderer._ModelMatrixNozzle, 0);
Matrix.multiplyMM(GLES20Renderer._MVPMatrixNozzle, 0, GLES20Renderer._ProjectionMatrix, 0, GLES20Renderer._MVPMatrixNozzle, 0);
GLES20Renderer._lastZAngle = zAngle;
}
Apk for download:
http://www.pixdip.com/opengles/rotation/rotation.apk
(Try swiping a longer horizontal area from extreme left to right to observe drift early. Please be patient! Drift can take 20 seconds to occur)
For those whom it did not happen, here is the automated apk:
http://www.pixdip.com/opengles/rotation/automatic.apk
and the edited part of code:
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentreMatrix[0], GLES20Renderer._uNozzleCentreMatrix[1], 0);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._zAngle, 0, 0, 1);
//Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._lastZAngle, 0, 0, 1);
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._uNozzleCentreMatrix[0], -GLES20Renderer._uNozzleCentreMatrix[1], 0);
Sometimes floating point errors get accumulated because of matrix stack.
This can be removed by using separate matrices for some critical transformations:
private static float[] _TMatrix = new float[16];
private static float[] _ModelMatrix = new float[16];
Matrix.setIdentity(Renderer._ModelMatrix);
Matrix.setIdentity(Renderer._TMatrix);
Matrix.translate(Renderer._ModelMatrix, xmov,ymov,0);
Matrix.translate(Renderer._TMatrix, -xmov,-ymov,0);
Matrix.multiply(Renderer._ModelMatrix, Renderer._TMatrix, Renderer._ModelMatrix);
// will result in an identity model matrix, without any floating point errors
Related
I am learning about 2d images. We were given a template to use and told do various things. Everything seems to be working fine, except the image is already being drawn rotated. I do not understand why. This is the image.
public static int[][] letterN = {
{0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0}};
This is the code from the program: I dont understand why the image is popping up rotated. All of the other letters are being rotated as well.
package cmsc325animate;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CMSC325Animate extends JPanel {
// A counter that increases by one in each frame.
private int frameNumber;
// The time, in milliseconds, since the animation started.
private long elapsedTimeMillis;
// This is the measure of a pixel in the coordinate system
// set up by calling the applyLimits method. It can be used
// for setting line widths, for example.
private float pixelSize;
static int translateX = 0;
static int translateY = 0;
static double rotation = 0.0;
static double scaleX = 1.0;
static double scaleY = 1.0;
ImageTemplate myImages = new ImageTemplate();
BufferedImage tImage = myImages.getImage(ImageTemplate.letterT);
BufferedImage nImage = myImages.getImageN(ImageTemplate.letterN);
BufferedImage oImage = myImages.getImageO(ImageTemplate.letterO);
BufferedImage aImage = myImages.getImageA(ImageTemplate.letterA);
BufferedImage hImage = myImages.getImageH(ImageTemplate.letterH);
BufferedImage heartImage = myImages.getImageHeart(ImageTemplate.heart);
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame window;
window = new JFrame("Java Animation"); // The parameter shows in the window title bar.
final CMSC325Animate panel = new CMSC325Animate(); // The drawing area.
window.setContentPane(panel); // Show the panel in the window.
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // End program when window closes.
window.pack(); // Set window size based on the preferred sizes of its contents.
window.setResizable(false); // Don't let user resize window.
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
window.setLocation( // Center window on screen.
(screen.width - window.getWidth()) / 2,
(screen.height - window.getHeight()) / 2);
Timer animationTimer; // A Timer that will emit events to drive the animation.
final long startTime = System.currentTimeMillis();
// Taken from AnimationStarter
// Modified to change timing and allow for recycling
animationTimer = new Timer(1600, new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (panel.frameNumber > 4) {
panel.frameNumber = 0;
} else {
panel.frameNumber++;
}
panel.elapsedTimeMillis = System.currentTimeMillis() - startTime;
panel.repaint();
}
});
window.setVisible(true); // Open the window, making it visible on the screen.
animationTimer.start(); // Start the animation running.
}
public CMSC325Animate() {
// Size of Frame
setPreferredSize(new Dimension(800, 600));
}
// This is where all of the action takes place
// Code taken from AnimationStarter.java but modified to add the specific Images
// Also added looping structure for Different transformations
protected void paintComponent(Graphics g) {
/* First, create a Graphics2D drawing context for drawing on the panel.
* (g.create() makes a copy of g, which will draw to the same place as g,
* but changes to the returned copy will not affect the original.)
*/
Graphics2D g2 = (Graphics2D) g.create();
/* Turn on antialiasing in this graphics context, for better drawing.
*/
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
/* Fill in the entire drawing area with white.
*/
g2.setPaint(Color.WHITE);
g2.fillRect(0, 0, getWidth(), getHeight()); // From the old graphics API!
/* Here, I set up a new coordinate system on the drawing area, by calling
* the applyLimits() method that is defined below. Without this call, I
* would be using regular pixel coordinates. This function sets the value
* of the global variable pixelSize, which I need for stroke widths in the
* transformed coordinate system.
*/
// Controls your zoom and area you are looking at
applyWindowToViewportTransformation(g2, -75, 75, -75, 75, true);
AffineTransform savedTransform = g2.getTransform();
System.out.println("Frame is " + frameNumber);
switch (frameNumber) {
case 1: // First frame is unmodified.
translateX = 0;
translateY = 0;
scaleX = 1.0;
scaleY = 1.0;
rotation = 0;
break;
case 2: // Second frame translates each image by (-10, 12).
translateX = -10;
translateY = 12;
break;
case 3: // Third frame rotates each image by 55 degrees Counter
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
break;
// Can add more cases as needed
case 4: // 4th frame rotates each image by 75 degress Clockwise
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
rotation = 75 * Math.PI * 180;
break;
case 5: // Scales image 3.5 for x and 1.5 for y
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
rotation = 75 * Math.PI * 180;
scaleX = scaleX * 3.5;
scaleY = scaleY * 1.5;
break;
default:
break;
} // End switch
g2.translate(translateX, translateY); // Move image.
// To offset translate again
g2.translate(-10,10);
g2.rotate(rotation); // Rotate image.
g2.scale(scaleX, scaleY); // Scale image.
g2.drawImage(tImage, 100, 0, this); // Draw image.
g2.setTransform(savedTransform);
// draw nImage
g2.translate(translateX, translateY); // Move image.
// To offset translate again
// This allows you to place your images across your graphic
g2.translate(-30, 30);
g2.rotate(rotation); // Rotate image.
g2.scale(scaleX, scaleY); // Scale image.
g2.drawImage(nImage, 0, 0, this); // Draw image.
g2.setTransform(savedTransform);
// You can add more shapes/images as needed
// draw o Image
g2.translate(translateX, translateY); //move image
g2.translate(-10, 10);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(oImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(10, -10);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(aImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(30, -30);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(hImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(50, -50);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(heartImage, 0, 0, this);
g2.setTransform(savedTransform);
}
// Method taken directly from AnimationStarter.java Code
private void applyWindowToViewportTransformation(Graphics2D g2,
double left, double right, double bottom, double top,
boolean preserveAspect) {
int width = getWidth(); // The width of this drawing area, in pixels.
int height = getHeight(); // The height of this drawing area, in pixels.
if (preserveAspect) {
// Adjust the limits to match the aspect ratio of the drawing area.
double displayAspect = Math.abs((double) height / width);
double requestedAspect = Math.abs((bottom - top) / (right - left));
if (displayAspect > requestedAspect) {
// Expand the viewport vertically.
double excess = (bottom - top) * (displayAspect / requestedAspect - 1);
bottom += excess / 2;
top -= excess / 2;
} else if (displayAspect < requestedAspect) {
// Expand the viewport vertically.
double excess = (right - left) * (requestedAspect / displayAspect - 1);
right += excess / 2;
left -= excess / 2;
}
}
g2.scale(width / (right - left), height / (bottom - top));
g2.translate(-left, -top);
double pixelWidth = Math.abs((right - left) / width);
double pixelHeight = Math.abs((bottom - top) / height);
pixelSize = (float) Math.max(pixelWidth, pixelHeight);
}
}
I started watching these tutorials for creating a 2d top-down game using LWJGL and I read that VBO's should be fast but for rendering 48*48 tiles per frame I get only about 100FPS which is pretty slow because I will add a lot more stuff to the game than just some static, not moving or changing, tiles.
What can I do to make this faster? Keep in mind that I just started learning lwjgl and opengl so I probably won't know many things.
Anyways, here are some parts of my code (I removed some parts from the code that were kinda meaningless and replaced them with some descriptions):
The main loop
double targetFPS = 240.0;
double targetUPS = 60.0;
long initialTime = System.nanoTime();
final double timeU = 1000000000 / targetUPS;
final double timeF = 1000000000 / targetFPS;
double deltaU = 0, deltaF = 0;
int frames = 0, updates = 0;
long timer = System.currentTimeMillis();
while (!window.shouldClose()) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
// --- [ update ] ---
--INPUT HANDLING FOR BASIC MOVEMENT, CLOSING THE GAME AND TURNING VSYNC ON AND OFF USING A METHOD FROM THE INPUT HANDLER CLASS--
world.correctCamera(camera, window);
window.update();
updates++;
deltaU--;
}
if (deltaF >= 1) {
// --- [ render ] ---
glClear(GL_COLOR_BUFFER_BIT);
world.render(tileRenderer, shader, camera, window);
window.swapBuffers();
frames++;
deltaF--;
}
--PRINTING THE FPS AND UPS EVERY SECOND--
}
The input handler methods used:
I have this in my constructor:
this.keys = new boolean[GLFW_KEY_LAST];
for(int i = 0; i < GLFW_KEY_LAST; i++)
keys[i] = false;
And here are the methods:
public boolean isKeyDown(int key) {
return glfwGetKey(window, key) == 1;
}
public boolean isKeyPressed(int key) {
return (isKeyDown(key) && !keys[key]);
}
public void update() {
for(int i = 32; i < GLFW_KEY_LAST; i++)
keys[i] = isKeyDown(i);
}
This is the render method from the World class:
public void render(TileRenderer renderer, Shader shader, Camera camera, Window window) {
int posX = ((int) camera.getPosition().x + (window.getWidth() / 2)) / (scale * 2);
int posY = ((int) camera.getPosition().y - (window.getHeight() / 2)) / (scale * 2);
for (int i = 0; i < view; i++) {
for (int j = 0; j < view; j++) {
Tile t = getTile(i - posX, j + posY);
if (t != null)
renderer.renderTile(t, i - posX, -j - posY, shader, world, camera);
}
}
}
This is the renderTile() method from TileRenderer:
public void renderTile(Tile tile, int x, int y, Shader shader, Matrix4f world, Camera camera) {
shader.bind();
if (tileTextures.containsKey(tile.getTexture()))
tileTextures.get(tile.getTexture()).bind(0);
Matrix4f tilePosition = new Matrix4f().translate(new Vector3f(x * 2, y * 2, 0));
Matrix4f target = new Matrix4f();
camera.getProjection().mul(world, target);
target.mul(tilePosition);
shader.setUniform("sampler", 0);
shader.setUniform("projection", target);
model.render();
}
This is the constructor and render method from Model class:
public Model(float[] vertices, float[] texture_coords, int[] indices) {
draw_count = indices.length;
v_id = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, v_id);
glBufferData(GL_ARRAY_BUFFER, createBuffer(vertices), GL_STATIC_DRAW);
t_id = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, t_id);
glBufferData(GL_ARRAY_BUFFER, createBuffer(texture_coords), GL_STATIC_DRAW);
i_id = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
IntBuffer buffer = BufferUtils.createIntBuffer(indices.length);
buffer.put(indices);
buffer.flip();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void render() {
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, v_id);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, t_id);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_id);
glDrawElements(GL_TRIANGLES, draw_count, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
I store the vertices, texture coords and indices in the tile renderer:
float[] vertices = new float[]{
-1f, 1f, 0, //top left 0
1f, 1f, 0, //top right 1
1f, -1f, 0, //bottom right 2
-1f, -1f, 0, //bottom left 3
};
float[] texture = new float[]{
0, 0,
1, 0,
1, 1,
0, 1,
};
int[] indices = new int[]{
0, 1, 2,
2, 3, 0
};
I don't know what else to put here but the full source code and resources + shader files are available on github here.
With your current system, what I would recommend doing is grouping your tiles based on texture. Create something like this:
Map<Texture, List<Tile>> tiles = new HashMap<Texture, List<Tile>>()
Then when you go to render your map of tiles, you will only need to set the texture once per group of tiles, rather than once per tile. This saves PCI-E bandwidth for pushing textures/texture ids to the GPU. You would achieve that like this (pseudo code):
for (Texture tex : tile.keySet())
{
BIND TEXTURE
for (Tile tile : tiles.get(tex))
{
SET UNIFORMS
RENDER
}
}
Something else I see along these lines is that you are pushing the projection matrix to each tile individually. When you are running a shader program, the value of a given uniform stays the same until you change it or until the program ends. Set the projection matrix uniform once.
It also appears that you are calling this every renderTile(...). Given the value does not change, calculate it once before the render pass, then pass it in as a variable in the renderTile(...) method rather than passing in camera and world.
I have 3 FloatBuffers - vertices, normals and colors which contain what the name suggests. I also have an IntBuffer to keep track of the indexing.
The data looks correct, but I'm having trouble displaying it. I just see a blank canvas. I'm not sure what I am doing wrong. I'm guessing something is being overlooked in init(), display(), reshape() and dispose(). Can anyone tell me if you find something glaringly wrong in the code below, and why you think nothing is being displayed?
#Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glClearColor(.0f, .0f, .2f, 0.9f);
gl.glEnable(GL2.GL_DEPTH_TEST);
gl.glDepthFunc(GL2.GL_LESS);
gl.glEnable(GL2.GL_CULL_FACE);
gl.glEnable(GL2.GL_LIGHTING);
gl.glEnable(GL2.GL_LIGHT0);
gl.glEnable(GL2.GL_AUTO_NORMAL);
gl.glEnable(GL2.GL_NORMALIZE);
gl.glFrontFace(GL2.GL_CCW);
gl.glCullFace(GL2.GL_BACK);
gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);
gl.glShadeModel(GL2.GL_SMOOTH);
if (viewMesh) {
gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, GL2.GL_LINE);
} else {
gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, GL2.GL_FILL);
}
glu = new GLU();
// Build the VBOs
VBO = IntBuffer.allocate(4);
gl.glGenBuffers(4, VBO);
vertVBOID = VBO.get(0);
normalVBOID = VBO.get(1);
colorVBOID = VBO.get(2);
indexVBOID = VBO.get(3);
// vertices
int vsize = sd.verts.capacity() * BufferUtil.SIZEOF_FLOAT;
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vertVBOID); // get a valid name
gl.glBufferData(GL2.GL_ARRAY_BUFFER, vsize, sd.verts, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); // reset
// normals
int nsize = sd.normals.capacity() * BufferUtil.SIZEOF_FLOAT;
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, normalVBOID);
gl.glBufferData(GL2.GL_ARRAY_BUFFER, nsize, sd.normals, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
// colors
int csize = sd.colors.capacity() * BufferUtil.SIZEOF_FLOAT;
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, colorVBOID);
gl.glBufferData(GL2.GL_ARRAY_BUFFER, csize, sd.colors, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
int isize = sd.indices.capacity() * BufferUtil.SIZEOF_INT;
gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, indexVBOID);
gl.glBufferData(GL2.GL_ELEMENT_ARRAY_BUFFER, isize, sd.indices, GL2.GL_STATIC_DRAW);
gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
// sd.verts = null; // copy of data is no longer necessary, it is in the graphics card now.
// sd.colors = null;
// sd.normals = null;
// sd.indices = null;
}
#Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
glu.gluLookAt(45, 0, 0, 0, 0, 0, 0.0, 1.0, 0.0);
gl.glScalef(scale, scale, scale);
gl.glRotatef(rot, 0, 1, 0);
gl.glTranslatef(-sd.tr_x, -sd.tr_y, -sd.tr_z);
gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl.glEnableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
gl.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, normalVBOID);
gl.glNormalPointer(GL2.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, colorVBOID);
gl.glColorPointer(3, GL2.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vertVBOID);
gl.glVertexPointer(3, GL2.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, indexVBOID);
gl.glDrawElements(GL2.GL_TRIANGLES, sd.indices.capacity(), GL2.GL_INT, 0);
gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0); // unbind it
gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
gl.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY);
}
#Override
public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
GL2 gl = drawable.getGL().getGL2();
double fov = (Math.PI / 4.0);
double zmm = Math.abs(sd.min_z - sd.max_z);
camdist = (zmm / 2.0) / Math.tan(fov / 2.0);
h = (h == 0) ? 1 : h;
gl.glViewport(0, 0, w, h);
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(camdist, w / (float) h, 0.1f, 1000.0);
}
/**
* Initilaize graphics
*/
public void initOGL() {
profile = GLProfile.get(GLProfile.GL2);
caps = new GLCapabilities(profile);
caps.setHardwareAccelerated(true);
canvas = new GLCanvas(caps);
canvas.addGLEventListener(this);
canvas.requestFocusInWindow();
getContentPane().add(canvas); // add to the frame
}
#Override
public void dispose(GLAutoDrawable drawable) {
}
One clear problem is in the draw call:
gl.glDrawElements(GL2.GL_TRIANGLES, sd.indices.capacity(), GL2.GL_INT, 0);
GL_INT is not valid for the type argument of glDrawElements(). The only valid values are GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT and GL_UNSIGNED_INT. So the call needs to be:
gl.glDrawElements(GL2.GL_TRIANGLES, sd.indices.capacity(), GL2.GL_UNSIGNED_INT, 0);
Anytime you have a problem with OpenGL code not working as expected, make sure that you call glGetError(). With the call from your code, you should immediately get a GL_INVALID_ENUM error.
Also, your perspective setup code looks somewhat suspicious. I don't fully understand what you're trying to do there, but even just the naming suggests a possible misunderstanding:
glu.gluPerspective(camdist, w / (float) h, 0.1f, 1000.0);
The first argument of gluPerspective() is the field-of-view angle (in degrees), not a distance.
I'm new in Andengine and somewhere I found that If you want use one BitmapTextureAtlas for several ButtonSprites/Sprites you have to do it like this
BitmapTextureAtlas texAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 316, 1062, TextureOptions.NEAREST_PREMULTIPLYALPHA);
for (int i = 0; i < 10; i++) {
TextureRegion pistolActive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolActive.set(0, 0, TILE_WIDTH, TILE_HEIGHT);
TextureRegion pistolUnactive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolUnactive.set(0, TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
TextureRegion pistolDeactive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolDeactive.set(0, TILE_HEIGHT * 2, TILE_WIDTH, TILE_HEIGHT);
texAtlas.load();
ButtonSprite button = new ButtonSprite(0, 0, pistolActive, pistolUnactive, pistolDeactive, activity.getVertexBufferObjectManager(), new ButtonSprite.OnClickListener() {
#Override
public void onClick(final ButtonSprite buttonSprite, float v, float v2) {
}
});
}
But now I check and if you put texAtlas.load() before loop-for it will work too. But what's the best practice ?
BitmapTextureAtlas texAtlas = new BitmapTextureAtlas(activity.getTextureManager(), 316, 1062, TextureOptions.NEAREST_PREMULTIPLYALPHA);
TextureRegion pistolActive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolActive.set(0, 0, TILE_WIDTH, TILE_HEIGHT);
TextureRegion pistolUnactive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolUnactive.set(0, TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
TextureRegion pistolDeactive = BitmapTextureAtlasTextureRegionFactory.createFromAsset(texAtlas, activity.getApplicationContext(), POWERUP_TEXTURE_PATH, 0, 0);
pistolDeactive.set(0, TILE_HEIGHT * 2, TILE_WIDTH, TILE_HEIGHT);
texAtlas.load();
for (int i = 0; i < 10; i++) {
ButtonSprite button = new ButtonSprite(0, 0, pistolActive, pistolUnactive, pistolDeactive, activity.getVertexBufferObjectManager(), new ButtonSprite.OnClickListener() {
#Override
public void onClick(final ButtonSprite buttonSprite, float v, float v2) {
}
});
}
No need to create texture regions multiple times.So do this manner.
Actually If you load in this way, you will get some UI problems.
So my suggestion is use different textures for different sprites to load
If you want to avoid loading problems then you can go for Sprite sheets(eg.. TexturePacker)
Cannot seem to get Vertex Attribute Arrays working properly for per vertex data.
Here's the SSCCE:
private static void createDisplay(int w, int h) {
try {
Display.create();
Display.setDisplayMode(new DisplayMode(w, h));
}
catch (LWJGLException e) {
e.printStackTrace();
}
float size = 1;
float aspect = (float) Display.getWidth() / Display.getHeight();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(-size * aspect, size * aspect, -size, size, -1, 1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
public static void main(String[] args) {
createDisplay(1200, 800);
GL11.glViewport(0, 0, Display.getWidth(), Display.getHeight());
ShaderManager.createShader("2Dv", new File("src/Shaders/2D.vert"), SHADER_VERT);
ShaderManager.createShader("2Df", new File("src/Shaders/2D.frag"), SHADER_FRAG);
ShaderManager.createProgram("2D", "2Dv", "2Df");
// Shader compiles and links correctly.
ShaderManager.useProgram("2D");
// Calls glUseProgram(programID);
float[] vertexData = new float[] {-0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f};
int vao = GL30.glGenVertexArrays();
if (vao == 0)
System.exit(-1);
GL30.glBindVertexArray(vao);
int vertexBuffer = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBuffer);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, BufferUtil.asDirectFloatBuffer(vertexData), GL15.GL_DYNAMIC_DRAW);
// GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
// GL11.glVertexPointer(2, GL11.GL_FLOAT, 2 * 4, 0);
int loc = ShaderManager.currentProgram.getAttribute("vertex");
if (loc == -1)
Debug.log(Debug.INSTANCE_MANAGEMENT, "Attribute [", "", "] not found in Shader [",
ShaderManager.currentProgram.toString(), "].");
else {
GL20.glVertexAttribPointer(loc, 2, GLCONST.TYPE_FLOAT, false, 2 * 4, 0);
GL20.glEnableVertexAttribArray(loc);
}
GL30.glBindVertexArray(0);
GL11.glColor3f(1, 0, 0);
GL11.glClearColor(0.5f, 0.5f, 0.8f, 1);
int indexBuffer = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, BufferUtil.asDirectFloatBuffer(new float[] {0, 1, 2, 3}),
GL15.GL_DYNAMIC_DRAW);
while (!Display.isCloseRequested()) {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
GL11.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0);
GL30.glBindVertexArray(0);
Display.update();
int error = GL11.glGetError();
if (error != GL11.GL_NO_ERROR)
System.out.println(GLU.gluErrorString(error));
}
}
The problem lies in the usage of vertex Attribute arrays. The old code I used was:
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glVertexPointer(2, GL11.GL_FLOAT, 2 * 4, 0);
The new version is:
int loc = ShaderManager.currentProgram.getAttribute("vertex");//Call to glGetAttribLocation();
if (loc == -1){
System.exit(-1);
}
GL20.glVertexAttribPointer(loc, 2, GL11.GL_FLOAT, false, 2 * 4, 0);
GL20.glEnableVertexAttribArray(loc);
The original code was the commented 2 lines. Upon running, this correctly sent vertex data to gl_Vertex and rendered a square of size 1.
The new code shuld send vertex data to the vertex attribute, but it gets nothing.
When the original code is uncommented and both old and new code used, both gl_Vertex and vertex attribute get vertex data.
What is going wrong here?
So I figured out the problem after a while. The problem is due to an AMD driver bug when using a OpenGL 3.0+ core profile.
The "vertex" attribute array was assigned a location of 1.
The bug occurs when the attribute array 0 is unused. Nothing is rendered if array 0 is not enabled.
To fix this probelm I simply explicitly assigned "vertex" to location 0.
layout(location = 0) in vec4 vertex;