I want to rotate the object z axis so I am using the below code but its not rotating at particular position its rotation just go back and appear come near. I think there is wrong with the values in GLU.gluLookAt(gl, 0, 0, 10, 0, 0, 0, 0, 1, 0);. Please help me to set the correct values of these So that rotation works well.
gl.glTranslatef(mOrigin.x, mOrigin.y, mOrigin.z);
gl.glRotatef(mRotate.x, 1f, 0f, 0f);
gl.glRotatef(mRotate.y, 0f, 1f, 0f);
gl.glRotatef(mRotate.z, 0f, 0f, 1f);
private class Renderer implements GLSurfaceView.Renderer {
public Renderer() {
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
setZOrderOnTop(true);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f,0.0f,0.0f, 0.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glShadeModel(GL10.GL_SMOOTH);
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
mViewWidth = (float)w;
mViewHeight = (float)h;
gl.glViewport(0,0,w,h);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45, mViewWidth/mViewHeight, 0.1f, 100f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glPushMatrix();
gl.glDisable(GL10.GL_DITHER);
GLU.gluLookAt(gl, 0, 0, 10, 0, 0, 0, 0, 1, 0);
//draw_model
gl.glPushMatrix();
if(mOrigin != null && mRotate != null) {
gl.glTranslatef(mOrigin.x, mOrigin.y, mOrigin.z);
gl.glRotatef(mRotate.x, 1f, 0f, 0f);
gl.glRotatef(mRotate.y, 0f, 1f, 0f);
gl.glRotatef(mRotate.z, 0f, 0f, 1f);
}
if(mModel != null) {
mModel.draw(gl, mContext);
if(!RendererView.textureFileName.equals(""))
mModel.bindTextures(mContext, gl);
}
gl.glPopMatrix();
gl.glPopMatrix();
if(isPictureTake) {
w = getWidth();
h = getHeight();
b = new int[w*(y+h)];
bt = new int[w*h];
IntBuffer ib = IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
createBitmapFromGLSurface(mContext);
isPictureTake = false;
}
}
}
ObjLoader.java
package com.amplimesh.models;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import com.amplimesh.renderer.RendererView;
import com.amplimesh.util.Point3;
/**
* Object Loader and draw the texture and object.
* #author Ajay
*/
public class ObjModel {
/**
* It fill the texture into the mesh
* #param context
* #param gl
*/
public void bindTextures(Context context, GL10 gl) {
Bitmap bitmap;
try {
InputStream is = context.getAssets().open("textures/"+RendererView.textureFileName);
bitmap = BitmapFactory.decodeStream(is);
if(bitmap != null) {
// generate one texture pointer
gl.glGenTextures(1, mTextures, 0);
// ...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);
// create nearest filtered texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
//gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
//gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
}
} catch (java.io.IOException e) {
return;
}
}
/**
* It draw the object.
* #param gl
*/
public void draw(GL10 gl, Context mContext) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
for (Model model : mModels) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, model.v);
if (model.vt != null && mTextures != null) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, model.vt);
}
if (model.vn != null) {
gl.glNormalPointer(GL10.GL_FLOAT, 0, model.vn);
}
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, model.v_size);
}
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
/**
* It Load the object from stream.
* #param is
* #param texture_name
* #return
* #throws IOException
*/
public static ObjModel loadFromStream(InputStream is, String texture_name) throws IOException {
ObjModel obj = ObjLoader.loadFromStream(is);
return obj;
}
private Model mModels[];
private int mTextures[] = new int[1];;
/**
* It read the the obj file.
* #author Ajay
*/
private static class ObjLoader {
public static ObjModel loadFromStream(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
ObjModel obj = new ObjModel();
ArrayList<Point3> v = new ArrayList<Point3>();
ArrayList<Point3> vt = new ArrayList<Point3>();
ArrayList<Point3> vn = new ArrayList<Point3>();
ArrayList<Face> f = new ArrayList<Face>();
ArrayList<Model> o = new ArrayList<Model>();
boolean o_pending=false;
while(reader.ready()) {
String line = reader.readLine();
if (line == null)
break;
StringTokenizer tok = new StringTokenizer(line);
String cmd = tok.nextToken();
if (cmd.equals("o")) {
if (o_pending) {
Model model = new Model();
model.fill(f, vt.size() > 0, vn.size() > 0);
o.add(model);
}
else {
o_pending=true;
}
}
else
if (cmd.equals("v")) {
v.add(read_point(tok));
}
else
if (cmd.equals("vn")) {
vn.add(read_point(tok));
}
else
if (cmd.equals("vt")) {
vt.add(read_point(tok));
}
else
if (cmd.equals("f")) {
if (tok.countTokens() != 3)
continue;
Face face = new Face(3);
while (tok.hasMoreTokens()) {
StringTokenizer face_tok = new StringTokenizer(tok.nextToken(), "/");
int v_idx = -1;
int vt_idx = -1;
int vn_idx = -1;
v_idx = Integer.parseInt(face_tok.nextToken());
if (face_tok.hasMoreTokens()) vt_idx = Integer.parseInt(face_tok.nextToken());
if (face_tok.hasMoreTokens()) vn_idx = Integer.parseInt(face_tok.nextToken());
//Log.v("objmodel", "face: "+v_idx+"/"+vt_idx+"/"+vn_idx);
face.addVertex(
v.get(v_idx-1),
vt_idx == -1 ? null : vt.get(vt_idx-1),
vn_idx == -1 ? null : vn.get(vn_idx-1)
);
}
f.add(face);
}
}
if (o_pending) {
Model model = new Model();
model.fill(f, vt.size() > 0, vn.size() > 0);
o.add(model);
}
obj.mModels = new Model[o.size()];
o.toArray(obj.mModels);
return obj;
}
private static Point3 read_point(StringTokenizer tok) {
Point3 ret = new Point3();
if (tok.hasMoreTokens()) {
ret.x = Float.parseFloat(tok.nextToken());
if (tok.hasMoreTokens()) {
ret.y = Float.parseFloat(tok.nextToken());
if (tok.hasMoreTokens()) {
ret.z = Float.parseFloat(tok.nextToken());
}
}
}
return ret;
}
}
private static class Face {
Point3 v[];
Point3 vt[];
Point3 vn[];
int size;
int count;
public Face(int size) {
this.size = size;
this.count = 0;
this.v = new Point3[size];
this.vt = new Point3[size];
this.vn = new Point3[size];
}
public boolean addVertex(Point3 v, Point3 vt, Point3 vn) {
if (count >= size)
return false;
this.v[count] = v;
this.vt[count] = vt;
this.vn[count] = vn;
count++;
return true;
}
public void pushOnto(FloatBuffer v_buffer, FloatBuffer vt_buffer, FloatBuffer vn_buffer) {
int i;
for (i=0; i<size; i++) {
v_buffer.put(v[i].x); v_buffer.put(v[i].y); v_buffer.put(v[i].z);
if (vt_buffer != null && vt[i] != null) {
vt_buffer.put(vt[i].x); vt_buffer.put(vt[i].y);
}
if (vn_buffer != null && vn[i] != null) {
vn_buffer.put(vn[i].x); vn_buffer.put(vn[i].y); vn_buffer.put(vn[i].z);
}
}
}
}
/**
* It hold the vertex buffer, vertex normal and texture.
* #author Ajay
*/
private static class Model {
public FloatBuffer v;
public FloatBuffer vt;
public FloatBuffer vn;
public int v_size;
public void fill(ArrayList<Face> faces, boolean has_tex, boolean has_normals) {
int f_len = faces.size();
this.v_size = f_len * 3;
ByteBuffer tBuf = ByteBuffer.allocateDirect(this.v_size*3 * 4);
tBuf.order(ByteOrder.nativeOrder());
this.v = tBuf.asFloatBuffer();
if (has_tex) {
ByteBuffer vtBuf = ByteBuffer.allocateDirect(this.v_size*3 * 4);
vtBuf.order(ByteOrder.nativeOrder());
this.vt = vtBuf.asFloatBuffer();
}
if (has_normals) {
ByteBuffer vnBuf = ByteBuffer.allocateDirect(this.v_size*3 * 4);
vnBuf.order(ByteOrder.nativeOrder());
this.vn = vnBuf.asFloatBuffer();
}
int i;
for (i=0; i < f_len; i++) {
Face face = faces.get(i);
face.pushOnto(this.v, this.vt, this.vn);
}
this.v.rewind();
if (this.vt != null)
this.vt.rewind();
if (this.vn != null)
this.vn.rewind();
}
}
}
To rotate an object at a specific position around an axis, you first need to translate the object's center to the origin. In your example, you have an object at (mOrigin) which may be relative to another translation (your camera's position).
Instead of translating your object to its final position and then rotating, you need to translate it to (0,0,0), rotate and then translate to the final position.
In the simplest case this would be:
gl.glRotatef (mRotate.x, 1f, 0f, 0f);
gl.glRotatef (mRotate.y, 0f, 1f, 0f);
gl.glRotatef (mRotate.z, 0f, 0f, 1f);
gl.glTranslatef (mOrigin.x, mOrigin.y, mOrigin.z);
In the more complicated case, where your object is relative to the camera, you would have to do something like this:
gl.glTranslatef (-Camera.x, -Camera.y, -Camera.z);
gl.glRotatef (mRotate.x, 1f, 0f, 0f);
gl.glRotatef (mRotate.y, 0f, 1f, 0f);
gl.glRotatef (mRotate.z, 0f, 0f, 1f);
gl.glTranslatef (Camera.x + mOrigin.x, Camera.y + mOrigin.y, Camera.z + mOrigin.z);
The rotation component of the Glulookat seems fine so I dont see any problem there that may cause issues.
First, from the link you provided, it seems to me that you want to rotate about the Y axis although you mentioned the rotation about the Z axis this can give different results. https://www.dropbox.com/s/ozt7beo4gz5q293/demo2__A.avi
Second, what are the values you are using within the m_Rotate vector? are they in degrees or radians?
third, are you pushing or popping the matrix stack? if so how? or are you feeding your own matrix data into the OpenGL?
also have you made sure to call glLoadIdentity() to set the view matrix back to identity matrix during your draw calls?
TBH there can be many areas that may cause issue. To narrow down the possible problem area, do the followings in your draw function;
stop using GluLookAt.
remove all push and pop calls,
firstly make sure to call
glLoadIdentity();
translate your camera behind the object (in your GLuLookAt this is set to 0,0,10).
glTranslatef(0.0f,0.0f,-10.0f);
then do simple rotation around the Z axis by;
glRotatef(mRotate.z, 0.0f, 0.0f, 1.0f);
EDIT: you call onDrawFrame to draw things on the scene as far as I can tell. . and in this call where you need to do these changes to figure out where your problem is.
instead of this;
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glPushMatrix();
gl.glDisable(GL10.GL_DITHER);
GLU.gluLookAt(gl, 0, 0, 10, 0, 0, 0, 0, 1, 0);
//draw_model
gl.glPushMatrix();
if(mOrigin != null && mRotate != null) {
gl.glTranslatef(mOrigin.x, mOrigin.y, mOrigin.z);
gl.glRotatef(mRotate.x, 1f, 0f, 0f);
gl.glRotatef(mRotate.y, 0f, 1f, 0f);
gl.glRotatef(mRotate.z, 0f, 0f, 1f);
}
...
gl.glPopMatrix();
gl.glPopMatrix();
}
try something simpler like this.
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glDisable(GL10.GL_DITHER);
gl.glMatrixMode(GL10.GL_MODELVIEW); //making sure OpenGL currently in model view
gl.glLoadIdentity(); //clear the model view matrix to identity matrix
gl.glTranslatef(0.0f, 0.0f, -10.0f); //move 10 unit backwards so as if camera moves backwards.
gl.glRotatef(90.0f,0.0f,0.0f,1.0f) //rotate 90 degree around the z axis.
//draw_model
...
//gl.glPopMatrix();
//gl.glPopMatrix();
}
by doing so your possible problematic area reduced alot. and you can start figuring out the problem. if the above code works, then possible errors reduced alot.
it could be that;
you setting the matrix mode other then model view somewhere else and not setting it back in your draw call,
you are not clearing the identity matrix in your draw call.
your origin and rotation values are not correctly done.
BTW: Im not an expert in java but in c++ and objective c, we can not do 1f to set a value to float. we must use 1.0f otherwise compiler will complain.
Related
Recently I worked on a 2D Game using SurfaceView and I learned a lot about game loops and everything. But now I want to make the same game using OpenGL. I read that the GLSurfaceView is the class that would be relative to what SurfaceView was. But I'm not sure about other things as:
Using SurfaceView, I used the Bitmap class to load a image resource that would be lets say a character. And that bitmap would be a property of my Character class. And for the gameloop I used a different Thread
I'm new to OpenGL so I'd like to know how to load image resources (do I load them using the Bitmap class?), or what to use instead of a Thread for the game loop?
do I load them using the Bitmap class?
Yes, you can load bitmap and use it as textImage2D.
Example:
public void loadTextures(GL10 gl, Context context) {
Log.e(LOG_TAG, "ExplosionSprite :: loadTextures");
mFrame = 0;
InputStream is;
Bitmap bitmap;
is = context.getResources().openRawResource(DRAW_SOURCE);
bitmap = BitmapFactory.decodeStream(is);
try {
is.close();
is = null;
} catch (IOException e) {
}
gl.glGenTextures(TEXTURE_COUNT, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
Before 4 month I did the same, because of performance switched to OpenGL ES 2D.
Its a bit complicated but after some time seems easy enough.
I don't have right now example for single image but have pretty good example for sprite sheet where I animate my Image. I'm sure you can remove not relevant data to make it for one static image. Google has enough resources "how to" so I'll point what I used for my purposes:
link 1
link 2
link 3
Cast_001_Sprite_.java
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import net.obviam.droidz.R;
import net.obviam.droidz.model.components.ESpriteDirection;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;
public class Cast_001_Sprite_ {
private static final String LOG_TAG = Cast_001_Sprite.class.getSimpleName();
/** Sprite sheet definition */
private static final int SPRITE_WIDTH = 5;
private static final int SPRITE_HEIGHT = 4;
private int DRAW_SOURCE = R.drawable.a1;
private int mX = Location._1[0]; // 100;
private int mY = Location._1[1]; // 100;
private float mScreenWidth, mScreenHeight, wRatio, hRatio;
private int mFrame = 0;
private int mSwitcher = 0;
private final static int TEXTURE_COUNT = 1; // for sprite sheet we use 1 image all the time.
private int[] textures = new int[TEXTURE_COUNT]; // frame animation
protected FloatBuffer vertexBuffer;
private final static ESpriteDirection mDirection = ESpriteDirection.TOP_TO_DOWN_LEFT_TO_RIGHT;
public float x, y, initPos, finalPos, initSpeed, currentPos;
private ByteBuffer bb1;
private final static int TOTAL_IMAGE_COUNT_IN_SPRITE = SPRITE_WIDTH * SPRITE_HEIGHT;
private FloatBuffer[] floatBufferArray = new FloatBuffer[TOTAL_IMAGE_COUNT_IN_SPRITE];
private float xOffset = 1.0f/SPRITE_WIDTH;
private float yOffset = 1.0f/SPRITE_HEIGHT;
float[] vertices = new float[] {
-1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f };
private float storage[][] = new float[TOTAL_IMAGE_COUNT_IN_SPRITE][];
private int[] sprite_X_Indexes = new int[SPRITE_WIDTH];//{1,2,3,4};
private int[] sprite_Y_Indexes = new int[SPRITE_HEIGHT];//{1,2,3,4};
public Cast_001_Sprite_(float screenWidth, float screenHeight){
generateSpriteIndexes();
updateScreenData(screenWidth, screenHeight);
int index = 0;
switch (mDirection) {
case TOP_TO_DOWN_LEFT_TO_RIGHT:
for(int row = 0; row<SPRITE_HEIGHT; row++){
for(int column = 0; column<SPRITE_WIDTH; column++){
storage[index] = generateTextures(column, row);
index++;
}
}
break;
case DOWN_TO_TOP_LEFT_TO_RIGHT:
//TODO
// for(int row = spriteLength; row>0; row--){
// for(int column = 0; column<spriteHeight; column++){
// storage[index] = generateTextures( row-1, column);
// index++;
// }
// }
break;
default:
break;
}
// vertices buffer
bb1 = ByteBuffer.allocateDirect(vertices.length * 4);
bb1.order(ByteOrder.nativeOrder());
vertexBuffer = bb1.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
for(int i=0; i<TOTAL_IMAGE_COUNT_IN_SPRITE; i++){
bb1 = ByteBuffer.allocateDirect(storage[i].length * 4);
bb1.order(ByteOrder.nativeOrder());
FloatBuffer textureBuffer = bb1.asFloatBuffer();
textureBuffer.put(storage[i]);
textureBuffer.position(0);
floatBufferArray[i] = textureBuffer;
}
}
private void generateSpriteIndexes() {
for(int indexX = 0; indexX<SPRITE_WIDTH; indexX++){
sprite_X_Indexes[indexX] = indexX+1;
}
for(int indexY = 0; indexY<SPRITE_HEIGHT; indexY++){
sprite_Y_Indexes[indexY] = indexY+1;
}
}
public void updateScreenData(float screenWidth, float screenHeight){
// takes screen Height and Width
this.mScreenWidth = (screenWidth > 0) ? screenWidth : 1f;
this.mScreenHeight = screenHeight;
wRatio = 10f/mScreenWidth;
hRatio = mScreenHeight/10f;
addExplosion(mX,mY);
}
public void addExplosion(float x, float y) {
this.x = x;
this.y = y;
this.initPos = y;
}
/**
* Generates texture by location
*
* #param texture - fill current texture
* #param placeX - image place in sprite scale X
* #param placeY - image place in sprite scale Y
* #return
*/
private float[] generateTextures(int placeX, int placeY) {
float texture[] = new float[8];
/*
V1 _____ V3
| |
| |
V2|_____|V4
*/
//StringBuffer buff = new StringBuffer();
/** V1 */
texture[0] = (placeX == 0)?0.0f : xOffset*sprite_X_Indexes[placeX-1];
texture[1] = yOffset*sprite_Y_Indexes[placeY];
/** V2 */
texture[2] = (placeX == 0)?0.0f : xOffset*sprite_X_Indexes[placeX-1];
texture[3] = (placeY == 0)?0.0f : yOffset*sprite_Y_Indexes[placeY-1];
/** V3 */
texture[4] = xOffset*sprite_X_Indexes[placeX];
texture[5] = yOffset*sprite_Y_Indexes[placeY];
/** V4 */
texture[6] = xOffset*sprite_X_Indexes[placeX];
texture[7] = (placeY == 0)?0.0f : yOffset*sprite_Y_Indexes[placeY-1];
return texture;
}
private void update() {
if(mSwitcher == 1){
mFrame = ++mFrame % TOTAL_IMAGE_COUNT_IN_SPRITE;
mSwitcher = 0;
// Log.e(LOG_TAG, "DevQuestSpriteBase :: " + mFrame);
}
else{
mSwitcher++;
}
}
public void reset(){
mFrame = 0;
}
public void loadTextures(GL10 gl, Context context) {
Log.e(LOG_TAG, "ExplosionSprite :: loadTextures");
mFrame = 0;
InputStream is;
Bitmap bitmap;
is = context.getResources().openRawResource(DRAW_SOURCE);
bitmap = BitmapFactory.decodeStream(is);
try {
is.close();
is = null;
} catch (IOException e) {
}
gl.glGenTextures(TEXTURE_COUNT, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
public void draw(GL10 gl){
// if(mFrame == TOTAL_IMAGE_COUNT_IN_SPRITE - 1){
// return;
// }
gl.glPushMatrix();
try {
float transx = + (wRatio * x);
float transy = + (mScreenHeight*wRatio) - (wRatio * y) - 1/hRatio;
// Log.e(LOG_TAG, "transx: " + transx + "; transy: " + transy + "; sprite.x: "+ sprite.x + "; sprite.y: " + sprite.y);
gl.glTranslatef(transx, transy, 0.0f);
//########### draw ##############
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, floatBufferArray[mFrame]);
update();
gl.glColor4f(1f, 1f, 1f, 0.2f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//###############################
} catch (NullPointerException e) {
}
gl.glPopMatrix();
}
}
DevQuest1Activity.java
public class DevQuest1Activity extends Activity {
private DevQuestGLSurfaceView mGLView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGLView = new DevQuestGLSurfaceView(this);
setContentView(mGLView);
}
#Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
#Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
}
DevQuestGLRenderer.java
public class DevQuestGLRenderer implements GLSurfaceView.Renderer {
private static final String LOG_TAG = "Fess";//DevQuestGLRenderer.class.getSimpleName();
private Context context;
private float ratio;
private int screenWidth, screenHeight;
public Cast_001_Sprite Cast_001_Sprite;
public DevQuestGLRenderer(Context context){
this.context = context;
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig confid) {
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.glClearDepthf(1.0f);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
gl.glEnable(GL10.GL_BLEND);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, /*GL10.GL_REPLACE*/ GL10.GL_MODULATE);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.e(LOG_TAG, "onSurfaceChanged");
// prevent 0 divise
if(height == 0) { height=1;}
screenWidth = width; screenHeight = height;
ratio = (float) width/height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, width, 0, height, -10f, 10f);
gl.glViewport(0, 0, screenWidth, screenHeight);
Cast_001_Sprite = new Cast_001_Sprite(width, height);
Cast_001_Sprite.loadTextures(gl, context);
}
#Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glScalef((screenWidth)/10, (screenHeight*ratio)/10, 1.0f);
Cast_001_Sprite.draw(gl);
gl.glPopMatrix();
}
}
DevQuestGLSurfaceView.java
public class DevQuestGLSurfaceView extends GLSurfaceView {
private DevQuestGLRenderer mRenderer;
private int count = 0;
public DevQuestGLSurfaceView(Context context) {
super(context);
mRenderer = new DevQuestGLRenderer(context);
setRenderer(mRenderer);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}
ESpriteDirection.java
public enum ESpriteDirection {
TOP_TO_DOWN_LEFT_TO_RIGHT,
DOWN_TO_TOP_LEFT_TO_RIGHT,
TOP_TO_DOWN_RIGHT_TO_LEFT,
DOWN_TO_TOP_RIGHT_TO_LEFT
}
And this is an image I used:
USE THIS CODE TO LOAD YOUR IMAGE..
public static int loadTexture(Context context, int resourceId) {
final int[] textureObjectIds = new int[1];
glGenTextures(1, textureObjectIds, 0);
if (textureObjectIds[0] == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "Could not generate a new OpenGL texture object.");
}
return 0;
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(
context.getResources(), resourceId, options);
if (bitmap == null) {
if (LoggerConfig.ON) {
Log.w(TAG, "Resource ID " + resourceId + " could not be decoded.");
}
glDeleteTextures(1, textureObjectIds, 0);
return 0;
}
// Bind to the texture in OpenGL
glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);
// Set filtering: a default must be set, or the texture will be
// black.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load the bitmap into the bound texture.
texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
// Note: Following code may cause an error to be reported in the
// ADB log as follows: E/IMGSRV(20095): :0: HardwareMipGen:
// Failed to generate texture mipmap levels (error=3)
// No OpenGL error will be encountered (glGetError() will return
// 0). If this happens, just squash the source image to be
// square. It will look the same because of texture coordinates,
// and mipmap generation will work.
glGenerateMipmap(GL_TEXTURE_2D);
// Recycle the bitmap, since its data has been loaded into
// OpenGL.
bitmap.recycle();
// Unbind from the texture.
glBindTexture(GL_TEXTURE_2D, 0);
return textureObjectIds[0];
}
Guys i need your help again :)
MainRenderer.java:
package com.example.galaga2d;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.ByteOrder;
import java.util.Random;
import java.util.Vector;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLUtils;
public class MainRenderer implements Renderer {
Random rand = new Random();
float chance = 0.0f;
private Context context;
public Ship playerShip = new Ship();
Vector<Asteroid> asteroidVector = new Vector<Asteroid>();
public MainRenderer(Context context) {
this.context = context;
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//! TEXTURES
playerShip.loadGLTexture(gl, this.context);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_SRC_COLOR);
//gl.glShadeModel(GL10.GL_SMOOTH);
//! TEXTURES
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
#Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glLoadIdentity();
chance = rand.nextFloat() * (100.0f - 1.0f) + 1.0f;
if (chance <= 4.0f) {
asteroidVector.addElement(new Asteroid());
}
if (playerShip.life != 0) {
playerShip.draw(gl);
gl.glLoadIdentity();
for (int i = 0; i < asteroidVector.size(); i++) {
if(asteroidVector.elementAt(i).textured == 0) {
asteroidVector.elementAt(i).loadGLTexture(gl, this.context);
asteroidVector.elementAt(i).textured |= 1;
//gl.glLoadIdentity();
} else {
asteroidVector.elementAt(i).textured &= ~1;
}
}
for (int i = 0; i < asteroidVector.size(); i++) {
asteroidVector.elementAt(i).collisionCheck();
asteroidVector.elementAt(i).draw(gl);
if (asteroidVector.elementAt(i).Y > 480.0f) {
asteroidVector.remove(i);
}
gl.glLoadIdentity();
}
} else {
gl.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
}
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, width, height, 0, 1, -1);
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
// --------------------------------------------------------------------------------
class Ship {
public int life = 3; // Количество жизней игрока
public FloatBuffer ShipVertexBuffer; // Vertex буффер
public FloatBuffer ShipTextureBuffer; // Texture буффер
public float X = 100.0f; // Начальные координаты игрока по X
public float Y = 300.0f; // Начальные координаты игрока по Y
//! TEXTURES
private int[] textures = new int[1];
//! TEXTURES
public float ShipVerticles[] = { // Вертикли прямоугольника - корабль
0, 0,
0, 30,
30, 0,
30, 30
};
//! TEXTURES
public float ShipTextures[] = { // Разметка наложения текстуры, соответствует
0.0f, 0.0f, // разметке вертиклей
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
//! TEXTURES
public Ship() {
//! Буффер вертексов
ByteBuffer bb = ByteBuffer.allocateDirect(36);
bb.order(ByteOrder.nativeOrder());
ShipVertexBuffer = bb.asFloatBuffer();
ShipVertexBuffer.put(ShipVerticles);
ShipVertexBuffer.position(0);
//! TEXTURES
bb = ByteBuffer.allocateDirect(ShipTextures.length * 4);
bb.order(ByteOrder.nativeOrder());
ShipTextureBuffer = bb.asFloatBuffer();
ShipTextureBuffer.put(ShipTextures);
ShipTextureBuffer.position(0);
//! TEXTURES
}
public void loadGLTexture(GL10 gl, Context context) {
// loading texture
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ship);
// generate one texture pointer
gl.glGenTextures(1, textures, 0);
// ...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
// create nearest filtered texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
}
public void draw(GL10 gl) {
//! TEXTURE
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//! TEXTURE
gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
gl.glTranslatef(playerShip.X, playerShip.Y, 0.0f);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, ShipVertexBuffer);
//! TEXTURE
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, ShipTextureBuffer);
//! TEXTURE
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
//! TEXTURE
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//! TEXTURE
}
}
class Asteroid {
private float colorR = rand.nextFloat()* (1.0f - 0.3f) + 0.3f;
private float colorG = rand.nextFloat()* (1.0f - 0.3f) + 0.3f;
private float colorB = rand.nextFloat()* (1.0f - 0.3f) + 0.3f;
private float X = rand.nextFloat() * (300.0f - 1.0f) + 1.0f;
private float Y = -30.0f;
private float size = rand.nextFloat() * (30.0f - 20.0f) + 20.0f;
private float speed = rand.nextFloat() * (10.0f - 1.0f) + 1.0f;
private int collision = 0;
private int textured = 0;
private FloatBuffer AsteroidVertexBuffer;
private FloatBuffer AsteroidTextureBuffer;
//! TEXTURES
private int[] textures = new int[1];
//! TEXTURES
public float AsteroidVerticles[] = {
0, 0, // лево низ
0, size, // лево вверх
size, 0, // право низ
size, size // право вверх
};
//! TEXTURES
public float AsteroidTextures[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
//! TEXTURES
public Asteroid() {
ByteBuffer bb = ByteBuffer.allocateDirect(36);
bb.order(ByteOrder.nativeOrder());
AsteroidVertexBuffer = bb.asFloatBuffer();
AsteroidVertexBuffer.put(AsteroidVerticles);
AsteroidVertexBuffer.position(0);
//! TEXTURES
bb = ByteBuffer.allocateDirect(AsteroidTextures.length * 4);
bb.order(ByteOrder.nativeOrder());
AsteroidTextureBuffer = bb.asFloatBuffer();
AsteroidTextureBuffer.put(AsteroidTextures);
AsteroidTextureBuffer.position(0);
//! TEXTURES
}
public void collisionCheck() {
float result = (float)Math.sqrt(Math.pow((playerShip.X-X), 2)+Math.pow((playerShip.Y-Y), 2));
if (result < size)
{
if(collision == 0)
{
playerShip.life = playerShip.life - 1;
collision |= 1;
}
} else {
collision &= ~1;
}
}
public void loadGLTexture(GL10 gl, Context context) {
// loading texture
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.asteroid);
// generate one texture pointer
gl.glGenTextures(1, textures, 0);
// ...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
// create nearest filtered texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
}
public void draw(GL10 gl) {
Y += speed;
//! TEXTURE
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//! TEXTURE
gl.glColor4f(colorR, colorG, colorB, 1.0f);
gl.glTranslatef(X, Y, 0.0f);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, AsteroidVertexBuffer);
//! TEXTURE
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, AsteroidTextureBuffer);
//! TEXTURE
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
//! TEXTURE
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//! TEXTURE
}
}
// --------------------------------------------------------------------------------
}
Every frame drawing object asteroid by chance:
chance = rand.nextFloat() * (100.0f - 1.0f) + 1.0f;
if (chance <= 4.0f) {
asteroidVector.addElement(new Asteroid());
}
Thats mean we need to load texture for all new objects we draw every second, but we dont need to load texture for one object many times, and i add flag to check is object textured or not:
for (int i = 0; i < asteroidVector.size(); i++) {
if(asteroidVector.elementAt(i).textured == 0) {
asteroidVector.elementAt(i).loadGLTexture(gl, this.context);
asteroidVector.elementAt(i).textured |= 1;
//gl.glLoadIdentity();
} else {
asteroidVector.elementAt(i).textured &= ~1;
}
}
After object created and textured, we need to delete it if he go over screen border, so i do this:
for (int i = 0; i < asteroidVector.size(); i++) {
asteroidVector.elementAt(i).collisionCheck();
asteroidVector.elementAt(i).draw(gl);
//! THIS
if (asteroidVector.elementAt(i).Y > 480.0f) {
asteroidVector.remove(i);
}
//! THIS
gl.glLoadIdentity();
}
But that not enough, because tuxture buffer dont clear, and after 10-20 seconds on application running i have see some lagging and low fps.
The question is - How i can clear texture buffer or memory? To fix lagging and low fps?
I suppose you could do that, but then again you shouldn't be getting yourself into a situation where you actually need to in the first place.
As it now stands, you're creating a new texture object (and never freeing it!) for every Asteroid that's ever constructed, which is absolutely not necessary - a great way to fix that is to use a shared texture object for all of your Asteroid instances.
In a static function (called once), simply decode the image resource to raw pixel data -> glGenTextures -> glBindTexture -> glTexParam... calls ->GLUtils.TexImage2D -> free the (client-side) decoded raw pixel data and store the texture id (given by glGenTextures) into a static variable within the Asteroid class. Having done this, you can just bind to this shared texture object at the rendering stage (ie. draw), and proceed as you normally would.
As said before, never calling glDeleteTextures is the second part of your problem, although now that you're (hopefully) using a shared texture object for your Asteroid instances, it becomes much less important.
(Furthermore, even the vertex/texcoord buffer can be shared between instances, provided they are identical :P)
I 'm trying to draw some triangles but for some reason I 'm only getting the background.
What do I miss?
Here is my code. It has 2 buffers that are created when onDrawFrame is called for the first time which load vertex and triangle data from an ArrayList or ArrayList I 've created by loading an .OBJ file.
public FloatBuffer fb = null; // To be loaded once in the first draw
public ShortBuffer ib = null;
#Override
public void onDrawFrame(GL10 gl)
{
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_LINE_SMOOTH);
gl.glEnable(GL10.GL_POINT_SMOOTH);
// Clear white
gl.glClearColor(1,0,0,1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// Look At
gl.glPushMatrix();
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -4, 0, 0, 0, 0,1.0f,0);
try
{
gl.glColor4f(1.0f,1.0f,0,1.0f);
int nv = SomeVertexArray.size();
int nt = SomeTriangleArray.size();
if (fb == null)
{
int TotalFBByteSize = 4*3*nv;
fb = ByteBuffer.allocateDirect(TotalFBByteSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
fb.clear();
for(int i = 0 ; i < SomeVertexArray.size() ; i++)
{
VERTEX v = SomeVertexArray.get(i);
fb.put(v.x);
fb.put(v.y);
fb.put(v.z);
}
}
if (ib == null)
{
int TotalTRByteSize = 4*3*nt;
ib = ByteBuffer.allocateDirect(TotalTRByteSize).order(ByteOrder.nativeOrder()).asShortBuffer();
ib.clear();
for(int i = 0 ; i < nt ; i++)
{
TRIANGLE v = SomeTriangleArray.get(i);
ib.put((short)v.i1);
ib.put((short)v.i2);
ib.put((short)v.i3);
}
}
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,fb);
gl.glDrawElements(GL10.GL_TRIANGLES, nt*3, GL10.GL_UNSIGNED_SHORT,ib);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
catch(Throwable ex)
{
ex.printStackTrace();
}
gl.glPopMatrix();
gl.glFlush();
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity(); //reset projection matrix
GLU.gluPerspective(gl,54.0f, (float)width/(float)height, 1.0f, 1000.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW); //set modelview matrix
gl.glLoadIdentity(); //reset modelview matrix
}
The problem was solved by using fb.put(float[]) instead of fb.put(float), i.e. by passing the entire vertice/triangle array to the direct buffer at once, and not within the loop.
I am not sure why, but it worked.
I want to overlay an icon on top of 3D models in my game. I'm using gluProject to get the screen coordinates of the centre point of the models, and then using that data to draw icons on a custom view:
In my renderer class:
private void projectScreenCoords(GLSurfaceView view, GraphicsEntity ge, GL10 gl){
MatrixGrabber matrixGrabber = new MatrixGrabber();
matrixGrabber.getCurrentModelView(gl);
float[] modelMat = matrixGrabber.mModelView;
matrixGrabber.getCurrentProjection(gl);
float[] projMat = matrixGrabber.mProjection;
gl.glMatrixMode(GL10.GL_MODELVIEW);
// view port
int[] viewport = new int[]{view.getTop(),view.getLeft(),view.getWidth(),view.getHeight()};
float[] vector = new float[4];
GLU.gluProject(ge.getTransform().tx, ge.getTransform().ty, ge.getTransform().tz, modelMat, 0, projMat, 0, viewport, 0, vector, 0);
ge.setScreenCoords(vector[0], viewport[3] - vector[1]);
}
and my custom view:
protected void onDraw (Canvas canvas){
Vector<GraphicsEntity> scene = renderer.getForegroundScene();
for(int i = 0;i<scene.size();i++){
GraphicsEntity ge = scene.get(i);
float[] xy = ge.getScreenCoords();
if(xy[0]>-1 && xy[1]>-1){
canvas.drawBitmap(bitmap, xy[0]-bitmapWidth/2, xy[1]-bitmapHeight/2, null);
}
invalidate();
}
}
and where I set my projection matrix:
protected void setProjectionMatrix(GL10 gl){
float ratio = (float) viewportWidth / viewportHeight;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio*zoom, ratio*zoom, -1*zoom, 1*zoom, nearPlane, farPlane);
}
However, the further from the centre of the screen, the further off the centre of the model the icon is drawn:
Clearly, if it was just the viewport size that was incorrect (due to the bars at the top/bottom) then the icons would be off just in the y-axis, but they are off in the x-axis as well. What am I missing / doing wrong? Also I haven't used the 3rd value returned by gluProject, but this always has a value of 0.7 so I'm not quite sure how it would be used if at all?
Using SDK version 7, I've tested this on multiple devices (ZTE Blade running android 2.1 and Kindle Fire running 2.3.4) with the same results. viewportWidth/Height and view.getWidth/Height() are the same, and view.getTop/Left() returns 0. The MatrixGrabber code works for gluUnProject, so I'm reasonably confident that isn't the problem
EDIT: here is the rest of my GL-related drawing code:
In renderer:
// camera variables
protected float FOV = 60.0f;
protected float nearPlane = 3;
protected float farPlane = 7;
protected float eyeX = 0;
protected float eyeY = 0;
protected float eyeZ = 0;
protected float centreX = 0;
protected float centreY = 0;
protected float centreZ = ((farPlane - nearPlane) / 2) + nearPlane;
protected float upX = 0;
protected float upY = 1;
protected float upZ = 0;
protected float viewportWidth;
protected float viewportHeight;
// user camera control variables
protected float zoom = 1;
protected float rotatedX = 0;
protected float rotatedY = 0;
protected boolean zoomed = true;
protected TrackingTransform tracking;
protected void setWorldTransform(GL10 gl){
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
// transforms the centre position to 0,0,0
gl.glTranslatef(-centreX, -centreY, -centreZ);
}
protected void setModelRotation(GL10 gl, GraphicsEntity ge){
if(ge.isTrackerball()){
gl.glRotatef(rotatedX, 1.0f, 0, 0);
gl.glRotatef(rotatedY, 0, -1.0f, 0);
if(tracking!=null){
synchronized(tracking){
tracking.transform(gl);
}
}
} else if(ge.isBackground()){
gl.glTranslatef(centreX, centreY, centreZ);
gl.glRotatef(rotatedX, 1.0f, 0, 0);
gl.glRotatef(rotatedY, 0, -1.0f, 0);
if(ge.isSkybox()==true){
ge.getTransform().sx = nearPlane + 1.0f;
ge.getTransform().sy = nearPlane + 1.0f;
ge.getTransform().sz = nearPlane + 1.0f;
ge.getTransform().tx = 0;
ge.getTransform().ty = 0;
ge.getTransform().tz = 0;
}
}
}
protected void setModelViewMatrix(GL10 gl){
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
// not used:
//GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, centreX, centreY, centreZ, upX, upY, upZ);
}
#Override
public void onDrawFrame(GL10 gl) {
// Set up various things before drawing
gl.glFrontFace(GL10.GL_CW);
gl.glEnable(GL10.GL_CULL_FACE);
gl.glCullFace(GL10.GL_FRONT);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL10.GL_DEPTH_TEST);
// change projection matrix
float oldzoom = zoom;
zoom = 1.0f;
setProjectionMatrix(gl);
zoom = oldzoom;
// set global world transform (also changes to modelview)
setWorldTransform(gl);
// loop through and draw background models
for(int i = 0;i<backgroundGraphicsEntities.size();i++){
GraphicsEntity ge = backgroundGraphicsEntities.get(i);
SimpleTransform t = ge.getTransform();
int modelIndex = ge.getModelIndex();
if(modelIndex>=0){
gl.glPushMatrix();
setModelRotation(gl, ge);
t.transform(gl);
if(ge.isSprite() && gl11flag){
Sprite s = sprites.get(modelIndex);
s.draw((GL11) gl, ge);
} else {
Model m = models.get(modelIndex);
if(m!=null){
m.draw(gl);
}
}
gl.glPopMatrix();
}
if(i==0 && ge.isSkybox()){
// if skybox, reset depth bit
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
}
}
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
// change projection matrix (if zoomed)
setProjectionMatrix(gl);
// change back to modelview
gl.glMatrixMode(GL10.GL_MODELVIEW);
// loop through and draw models
for(int i = 0;i<graphicsEntities.size();i++){
GraphicsEntity ge = graphicsEntities.get(i);
SimpleTransform t = ge.getTransform();
int modelIndex = ge.getModelIndex();
if(modelIndex>=0){
gl.glPushMatrix();
setModelRotation(gl, ge);
t.transform(gl);
if(ge.isSprite() && gl11flag){
Sprite s = sprites.get(modelIndex);
s.draw((GL11) gl, ge);
} else {
Model m = models.get(modelIndex);
if(m!=null){
m.draw(gl);
}
if(projectScreenCoords){
projectScreenCoords(glSurfaceView, ge, gl);
}
}
gl.glPopMatrix();
}
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
viewportWidth = width;
viewportHeight = height;
gl.glViewport(0, 0, width, height);
setProjectionMatrix(gl);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
if(gl instanceof GL11){
gl11flag = true;
}
gl.glClearColor(0, 0, 0, 1);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthMask(true);
//gl.glClearDepthf(1.0f);
gl.glDepthFunc(GL10.GL_LEQUAL);
//gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
setProjectionMatrix(gl);
}
and then in SimpleTransform (i.e. what gets called when ge.getTransform().transform(gl) is called):
public void transform(GL10 gl) {
gl.glTranslatef(tx, ty, tz);
gl.glRotatef(rz, 0, 0, 1);
gl.glRotatef(ry, 0, 1, 0);
gl.glRotatef(rx, 1, 0, 0);
gl.glScalef(sx, sy, sz);
}
and for TrackingTransform:
#Override
public void transform(GL10 gl) {
gl.glTranslatef(-tx, -ty, -tz);
}
and finally in model.draw():
public void draw(GL10 gl){
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Pass the vertex buffer in
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,
vertices);
int textureID = material.getTexture().getTextureID();
if(textureID>=0){
// Enable Textures
gl.glEnable(GL10.GL_TEXTURE_2D);
// Get specific texture.
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
// Use UV coordinates.
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Pass in texture coordinates
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureCoordinates);
}
// Pass in vertex normals
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glDrawElements(GL10.GL_TRIANGLES, numindices,GL10.GL_UNSIGNED_SHORT, indices);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
if(textureID>=0){
// Disable buffers
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
The problem wasn't in the gluProject code at all. In fact, it was to do with the translations done before calling gluProject:
In onDrawFrame:
Model m = models.get(modelIndex);
if(m!=null){
m.draw(gl);
gl.glTranslatef(-tracking.getTransform().tx, -tracking.getTransform().ty, -tracking.getTransform().tz);
if(tickcount % projectFrequency == 0 ){
projectScreenCoords(glSurfaceView, ge, gl);
}
}
I'm trying to render a 640x480 RGB565 image using OpenGL ES on Android using GLSurfaceView and Native C code.
Initially I had a 0x0501 error with glTexImage2D, which I was able to resolve by changing the image dimensions.
But now, in the "drawFrame" call, when I do glDrawTexiOES to resnder the texture, I'm getting the following error on the Logs:
drawtex.c:89: DrawTexture: No textures enabled
I'm already doing glEnable(GL_TEXTURE_2D), is there anything else I should do?
Is there a complete example showing GLSurfaceView with native code using textures?
Thanks in advance!
I have had the same problem,you can either make a custom renderer like this:
or (a do it little bit complicated by subclassing SurfaceView directly)
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.util.Log;
import com.TIEmulator.JNI.NLib;
class EmulatorRenderer implements GLSurfaceView.Renderer, NLib.EventsInterface {
public static interface RenderCb {
public boolean dismissStartupDialog();
public void updateStartupDialog(String msg);
}
private static int mViewHeight;
private static int mViewWidth;
private static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
private static int TEX_BUF_HEIGHT = 128;
private static int TEX_BUF_WIDTH = 256;
private final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4
* EmulatorRenderer.TEX_BUF_HEIGHT * EmulatorRenderer.TEX_BUF_WIDTH);
private boolean emulation_running = false;
private Context mContext = null;
private final int[] mCropWorkspace;
private final GLSurfaceView mGLView;
protected int mTex = -1;
protected int[] mtexBuf = new int[1];
private final int[] mTextureNameWorkspace;
public EmulatorRenderer(final Context ctx, final GLSurfaceView v) {
// Pre-allocate and store these objects so we can use them at runtime
// without allocating memory mid-frame.
mTextureNameWorkspace = new int[1];
mCropWorkspace = new int[4];
byteBuffer.order(ByteOrder.BIG_ENDIAN);
// Set our bitmaps to 16-bit, 565 format.
EmulatorRenderer.sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mGLView = v;
mContext = ctx;
try {
NLib.setListener(this);
} catch (final RuntimeException exc) {
exc.printStackTrace();
callFinishOnce();
return;
}
if (!NLib.TryLoadLib()) {
callFinishOnce();
return;
}
Log.v(this.getClass().toString(),">>>>>>>>>>>>>>>>>>>>>>> init successfull! <<<<<<<<<<<<<<<<<<<<<<");
}
private void callFinishOnce() {
if (mContext != null) {
((Activity) mContext).finish();
mContext = null;
}
}
#Override
protected void finalize() throws Throwable {
NLib.stopEmu();
super.finalize();
}
protected int loadBB(final GL10 gl) {
int textureName = -1;
if (mContext != null && gl != null) {
gl.glGenTextures(1, mTextureNameWorkspace, 0);
textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_NEAREST);// GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
mCropWorkspace[0] = 0; // u
// mCropWorkspace[1] = EMU_HEIGHT; // v
mCropWorkspace[1] = 0; // v
mCropWorkspace[2] = NLib.getWidth(); // w
// mCropWorkspace[3] = -EMU_HEIGHT; // h -EMU_HEIGHT;
mCropWorkspace[3] = NLib.getHeight(); // h -EMU_HEIGHT;
byteBuffer.order(ByteOrder.BIG_ENDIAN);
final int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
Log.e("SpriteMethodTest", "Texture Load GLError: " + error);
}
}
return textureName;
}
public void onDrawFrame(final GL10 gl) {
// Log.v(this.toString(),"onDrawFrame called");
gl.glActiveTexture(mTex);
gl.glClientActiveTexture(mTex);
byteBuffer.position(0);
// this two lines bind and create the texture!
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTex);
((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0);
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA,
EmulatorRenderer.TEX_BUF_WIDTH,
EmulatorRenderer.TEX_BUF_HEIGHT, 0, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, byteBuffer);
((GL11Ext) gl).glDrawTexiOES(0, 0, 0, EmulatorRenderer.mViewWidth,
EmulatorRenderer.mViewHeight);
/** gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL);
*/
}
public void OnFatalError(final String text) {
Log.d(toString(), "FATAL ERROR CALLBACK raised: " + text
+ " ===> Activity calls finish()");
}
public void onFinish() {
onPause();
}
/*
* JNI interface
*
*/
#Override
public void OnBufferUpdate() {
mGLView.requestRender();
}
// TODO Auto-generated method stub
#Override
public void OnWarning(String msg) {
// TODO Auto-generated method stub
}
public void onPause() {
mGLView.onPause();
// Log.v("onPause", "NILib.stopEmulate()");
emulation_running = false;
//startupDialogDismiss = false;
NLib.stopEmu();
}
public void onResume() {
// Log.v(this.toString(),"EmulatorRenderer:onResume called");
NLib.startEmu(byteBuffer);
mGLView.onResume();
//callFinishOnce();
emulation_running = true;
}
public void onSurfaceChanged(final GL10 gl, final int w, final int h) {
EmulatorRenderer.mViewWidth = w;
EmulatorRenderer.mViewHeight = h;
Log.v(toString(), "onSurfaceChanged: ==> New View Size: [" + w + ","
+ h + "]");
}
public void onSurfaceCreated(final GL10 gl, final EGLConfig config) {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
gl.glShadeModel(GL10.GL_FLAT);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
/*
* By default, OpenGL enables features that improve quality but reduce
* performance. One might want to tweak that especially on software
* renderer.
*/
gl.glDisable(GL10.GL_DITHER);
gl.glDisable(GL10.GL_LIGHTING);
gl.glDisable(GL10.GL_BLEND);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST); // Set Line
// Antialiasing
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// create the one and only texture here...
mTex = loadBB(gl);
}
public void OnTIEmuStopped() {
System.out.println("OnTIEmuStopped callback called! ");
callFinishOnce();
}
/* Called when the size of the window changes. */
public void sizeChanged(final GL10 gl, final int w, final int h) {
// Log.v(this.toString(),"sizeChanged: ==> new Viewport: ["+w+","+h+"]");
gl.glViewport(0, 0, w, h);
/*
* Set our projection matrix. This doesn't have to be done each time we
* draw, but usually a new projection needs to be set when the viewport
* is resized.
*/
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
// Set up the orthographic projection
// gl.glOrthof(0.0f, w, 0.0f, h, 0.0f, 1.0f);
gl.glOrthof(0.0f, w, 0.0f, 0.0f, h, 1.0f);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glMatrixMode(GL10.GL_MODELVIEW);
}
}
yes i did...
gl.glGenTextures(1, mTextureNameWorkspace, 0);
textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_NEAREST);// GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
mCropWorkspace[0] = 0; // u
// mCropWorkspace[1] = EMU_HEIGHT; // v
mCropWorkspace[1] = 0; // v
mCropWorkspace[2] = NLib.getWidth(); // w
// mCropWorkspace[3] = -EMU_HEIGHT; // h -EMU_HEIGHT;
mCropWorkspace[3] = NLib.getHeight(); // h -EMU_HEIGHT;
static_test_debug_if_gl_error( gl , "loadBB time 1");
gl.glActiveTexture(mTex);
gl.glClientActiveTexture(mTex);
static_test_debug_if_gl_error( gl , "loadBB time 2");
Did you generate a texture id and bind a texture first?
glGenTexture()/glBindTexture()
The following will get the texture all set up and ready to use on GL_TEXTURE0:
When loading the texture:
// in your native onSurfaceCreated function:
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
// setup texture parameters if you want:
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // etc.
glTexImage2D(GL_TEXTURE_2D, ... );
see here for how to fill out the TexImage2D parameters.
I've unfortunately not been able to get the glDrawTex_OES functions to work properly, but the texture does work if you render it onto a quad:
// in your native onRender function:
glBindTexture(GL_TEXTURE_2D, sGlTexture.texID);
// quad drawing:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, quadVerts[i]);
glTexCoordPointer(2, GL_FLOAT, 0, quadTexCoords[i]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);