I'm attempting to get a texture to show up on on a square made from a triangle fan, the texture is made from a Canvas.
The main color is just yellow and a smaller box is drawn inside of it, but the final texture is just solid yellow.
Yellow square with no texture (picture)
Fragment shadder:
public static final String fragmentShaderCode_TEXTURED =
"precision mediump float;" +
"varying vec2 v_texCoord;" +
"uniform sampler2D s_texture;" +
"void main() {" +
//"gl_FragColor = vColor;"+
" gl_FragColor = texture2D( s_texture, v_texCoord );" +
"}";
Texture generation:
public static int loadGLTexture(String s){
Rect r = new Rect();
ThreadDat.get().paint.getTextBounds(s, 0, 1, r); //get string dimensions, yeilds 8x9 pxls
Bitmap bitmap = Bitmap.createBitmap(bestSize(r.width()),bestSize(r.height()), Bitmap.Config.ARGB_8888);
//example size is 16x16pxls
Log.i("TextureSize", r.width() + " " + r.height());
Canvas c = new Canvas(bitmap);
//some temporary test code setting the background yellow
//Paint colors are stored per thread, only one right now
ThreadDat.get().paint.setARGB(255, 255, 255, 0);
c.drawRect(0, 0, c.getWidth(), c.getHeight(), ThreadDat.get().paint);
//type the letter, in this case "A" in blue
ThreadDat.get().paint.setARGB(255, 0, 0, 255);
ThreadDat.get().paint.setTypeface(Typeface.create("Consolas", Typeface.NORMAL));
c.drawText(s.charAt(0) + "", 0, 0, ThreadDat.get().paint);
//draw another square that is half width and height, should be Blue
c.drawRect(0, 0, c.getWidth() / 2, c.getHeight() / 2, ThreadDat.get().paint);
return loadTexture(bitmap);
}
Draw code:
#Override
public void draw() {
//clearing any error to check if program has an error
GLES20.glGetError();
//get the compiled shader for textured shapes
int prgm = MyGLRenderer.getSTRD_TXTR_SHDR();
GLES20.glUseProgram(prgm);
//check for new errors and log to logcat (nothing)
MyGLRenderer.logError();
//setup projection view matrix
float[] scratch = new float[16];
Matrix.setIdentityM(scratch, 0);
Matrix.multiplyMM(scratch, 0, MyGLRenderer.getmMVPMatrix(), 0, scratch, 0);
//apply translations to matrix
Matrix.translateM(scratch, 0, xOffset, yOffset, zOffset);
Matrix.setRotateEulerM(scratch, 0, yaw, pitch, roll);
//get vPosition variable handle from chosen shader
mPosHandle = GLES20.glGetAttribLocation(prgm, "vPosition");
GLES20.glEnableVertexAttribArray(mPosHandle);
GLES20.glVertexAttribPointer(mPosHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, VERTEX_STRIDE, vertexBuffer);
////pass color data (set to white)
//mColorHandle = GLES20.glGetUniformLocation(prgm, "vColor");
//GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//use texture0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//use texture from -> int textureID = MyGLRenderer.loadGLTexture("A");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
//get handel for "uniform sampler2D s_texture;" to set value
int txtureHandle = GLES20.glGetUniformLocation(prgm, "s_texture");
GLES20.glUniform1i(txtureHandle, 0); //set s_texture to use binded texture 0
//pass in texture coords (u,v / s,t)
int textureCoordHndl = GLES20.glGetAttribLocation(prgm, "a_texCoord");
GLES20.glVertexAttribPointer(textureCoordHndl, 2/*size, 2 points per vector*/,
GLES20.GL_FLOAT, false, 0, textureBuffer);
//pass in the model view projection matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(prgm, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, scratch, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertex_count);
GLES20.glDisableVertexAttribArray(mPosHandle);
MyGLRenderer.logError();
}
I tried using the same coordinate set as used in this example:
Vertices for square:
{ 0, 0, 0, //bottom left
0, height, 0, //topLeft
width, 0, 0, // bottom Right
width, height, 0)}; //topRight
Texture coords:
0.0f, 1.0f, // top left (V2)
0.0f, 0.0f, // bottom left (V1)
1.0f, 1.0f, // top right (V4)
1.0f, 0.0f // bottom right (V3)
Similar Issue
This does sound like there is an issue with texture coordinates. Since the whole thing is yellow I would suspect that the v_texCoord is always (0,0) in your fragment shader so the first texture pixel is being repeated.
The texture itself seems to be ok since the color is being drawn. Without the texture you would most likely see a black rectangle.
Anyway to handle such issues you need to be a bit inventive in debugging, testing. For testing the coordinates use gl_FragColor = vec4( v_texCoord.x, v_texCoord.y, .0, 1.0 );. This should output a gradient rectangle where top left is black, top right is red, bottom left is green. If you do not see this result then your texture coordinates are incorrect. In this case first check if the varying is correctly connected from the vertex shader. You may use v_texCoord = vec2(1.0, 0.0) in the vertex shader and the result should be a red rectangle (assuming you still have the previous test in the fragment shader). If the rectangle is red then the issue is most likely in your handles and not in the shaders (otherwise the varying is incorrectly set. Maybe a mismatch in naming). Check what is the value of the handle textureCoordHndl. If this is a negative value then the handle was not connected. This is most likely due to a mismatch in the naming.
From further inspection:
You are missing the enabling of the attribute for texture coordinates GLES20.glEnableVertexAttribArray(textureCoordHndl);. Remember that each of the attributes must be enabled before you use them.
Related
I drew a cube in openGL es 2.0.
Right now it has just two faces, for testing purposes(the front and the back). So basically there are two planes in space, both with the same color.
Now I want to apply a different color to each face. I tought that expanding the color array was sufficient, but the colors are not changing (there's just the original color).
Do I have to change the shader? Or pass a specific function to the draw method?
The class should explain better
public class Cube {
private FloatBuffer mVer;
private FloatBuffer colMem;
private ShortBuffer ordVer;
private float vertici[] = {
-0.2f, 0.2f, 0.2f, //p1 upper left front plane (0)
-0.2f, -0.2f, 0.2f, //p2 lower left front plane (1)
0.2f, -0.2f, 0.2f, //p3 lower right front plane (2)
0.2f, 0.2f, 0.2f, //p4 upper right front plane (3)
-0.2f, 0.2f, -0.2f, //p1 upper left front plane (4)
-0.2f, -0.2f, -0.2f, //p2 lower left front plane (5)
0.2f, -0.2f, -0.2f, //p3 lower right front plane (6)
0.2f, 0.2f, -0.2f, //p4 upper right front plane (7)
};
private short order[] = {
0, 1, 2, 0, 2, 3, //front face
7, 6, 5, 7, 5, 4, //back face
//3, 2, 6, 3, 6, 7, //right face
// 0, 1, 5, 0, 5, 4 //left face
};
private float color [] = {
0.8f, 0.8f, 0.1f, 1.0f,//color1
0.8f, 0.8f, 0.1f, 1.0f,
0.8f, 0.8f, 0.1f, 1.0f,
0.8f, 0.8f, 0.1f, 1.0f,
0.1f, 0.2f, 0.5f, 1.0f,//color2
0.1f, 0.2f, 0.5f, 1.0f,
0.1f, 0.2f, 0.5f, 1.0f,
0.1f, 0.2f, 0.5f, 1.0f
};
private final String vertCode =
"uniform mat4 uMVPMatrix;"+
"attribute vec4 vPosition;"+
"void main() {"+
"gl_Position = uMVPMatrix * vPosition;"+
"}";
private final String fragCode =
"precision mediump float;"+
"uniform vec4 vColor;"+
"void main() {"+
"gl_FragColor = vColor;"+
"}";
private int prog;
private int pos;
private int col;
private int mHandle;
public Cube () {
mVer = ByteBuffer.allocateDirect(vertici.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mVer.put(vertici).position(0);
ordVer = ByteBuffer.allocateDirect(order.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();
ordVer.put(order).position(0);
colMem = ByteBuffer.allocateDirect(color.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
colMem.put(color).position(0);
int vertexShader = Render.loadShader (GLES20.GL_VERTEX_SHADER, vertCode);
int fragmentShader = Render.loadShader (GLES20.GL_FRAGMENT_SHADER, fragCode);
prog = GLES20.glCreateProgram();
GLES20.glAttachShader(prog, vertexShader);
GLES20.glAttachShader(prog, fragmentShader);
GLES20.glLinkProgram(prog);
}
public void draw (float[] mVMatrix) {
GLES20.glUseProgram(prog);
pos = GLES20.glGetAttribLocation(prog, "vPosition");
GLES20.glEnableVertexAttribArray(pos);
GLES20.glVertexAttribPointer(pos, 3, GLES20.GL_FLOAT, false, 12, mVer);
col = GLES20.glGetUniformLocation(prog, "vColor");
GLES20.glUniform4fv(col, 1, color, 0);
mHandle = GLES20.glGetUniformLocation(prog, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mHandle, 1, false, mVMatrix, 0);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, order.length, GLES20.GL_UNSIGNED_SHORT, ordVer);
GLES20.glDisableVertexAttribArray(pos);
}
}
You have a few options:
If you want to pass in the color as a uniform, which seems to be where you were headed, you need to draw each face with a separate draw call. You can't just pass in an array of colors for the uniform, and expect the colors to be applied to the triangles in order. You would call glUniform4v with the first color, call glDrawElements with just the indices f the first face, and then repeat these two calls for each face. This is fairly inefficient.
You make the colors an attribute instead of a uniform, very similar to what you do for the vertex positions. You have to be careful when using this approach because you need an OpenGL vertex for each combination of position and color. For a cube, you typically end up with 24 vertices. You should be able to find details if you search for older questions about similar topics.
There's another method called "instanced rendering" that could be applied, but that is only available in ES 3.0.
I can use the below code to scale and translate a square using OpenGLES. But I'm not sure how to calculate the translation and scale factors. For example using the below picture of the OpenGL coordinate system and Matrix.translateM(mViewMatrix, 0, .5f, 0, 0); I would expect the square to be drawn halfway to the right of the screen, but instead it's drawn halfway to the left from the center. However Matrix.translateM(mViewMatrix, 0, 0, .5f, 0); does translate the square halfway up the screen from the center.
How would I translate and scale in order to programmatically draw N squares side by side horizontally filling the top of the screen?
#Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Translate by some amount
// Matrix.translateM(mViewMatrix, 0, ?, ?, 0);
// Scale by some amount
// Matrix.scaleM(mViewMatrix, 0, ?, ?, 1);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
// Draw square
mSquare.draw(mMVPMatrix);
}
I am not sure why you would need translate and scale to fill up a row of squares. To get rows of squares programmatically in openGL ES I would just make a bunch of squares initialized where you want them. An edited snippet from one of my projects went something like this:
public void onSurfaceCreated(GL10 unused, EGLConfig config){
GLES20.glClearColor(bgr, bgg, bgb, bga);
float z=0.0f;
float y=0.0f;
for(float i=(-worldwidth);i<worldwidth;i+=(cellwidth)){
square=new Square(i,y,z);
cellvec.addElement(square);
}
}
public void onDrawFrame(GL10 unused) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
for(int i=0;i<cellvec.size();i++){
cellvec.elementAt(i).draw(mMVPMatrix);
}
}
I am not entirely sure if something like this is what your looking for but it it seems to get the result of a row of squares like you wanted.
I'm developing an application for Android that uses OpenGL ES 2.0
Since it's my first time with OpenGL (I used to use WebGL), I made a custom and pretty simple API like THREE.js, which consists of a Object3D and Geometry objects.
Basicaly, what I did was: Store shapes inside the Geometry object, and create Mesh objects with the the geometry instance inside. Also, inside Mesh, I have: Vector3 object for: position, scale, rotation.
I created a circle to test, and here is what is happening
If I don't change ANY thing, the circle is perfect on the screen. If I change the vertices positions on the creation of the circle, the circle is still Ok also.
But, when I do some transformation (change the attribute position, scale or rotation) or Object3D (in this case, Mesh), the circle becomes "strech".
So, I think that there is some problem with the projectionMatrix, but the circle it's ok if I don't transform it.
Is there a problem with my matrix code? Should I send the Rotation, Translation and Scale matrix to the GPU?
Perhaps I'm complicating things, but since this is the first time I use OpenGL after reading lot's of information, it's acceptable...
Here is the Object3D code:
public class Object3D {
public Vector3 position = new Vector3();
public Vector3 rotation = new Vector3();
public Vector3 scale = new Vector3();
public Color color = new Color();
public float[] getMVMatrix(){
// Initialize matrix with Identity
float[] mvMatrix = new float[16];
Matrix.setIdentityM(mvMatrix, 0);
// apply scale
Matrix.scaleM(mvMatrix, 0, scale.x, scale.y, scale.z);
// set rotation
Matrix.setRotateM(mvMatrix, 0, rotation.x, 1f, 0, 0);
Matrix.setRotateM(mvMatrix, 0, rotation.y, 0, 1f, 0);
Matrix.setRotateM(mvMatrix, 0, rotation.z, 0, 0, 1f);
// apply translation
Matrix.translateM(mvMatrix, 0, position.x, position.y, position.z);
return mvMatrix;
}
}
This is the Geometry class, that simplifies the use of Triangles:
public class Geometry {
// Public, to allow modifications
public ArrayList<Vector3> vertices;
public ArrayList<Face3> faces;
// Type of Geometry
public int triangleType = GLES20.GL_TRIANGLES;
[...]
public FloatBuffer getVerticesBuffer(){
if(verticesBuffer == null || verticesBufferNeedsUpdate){
/*
* Cache faces
*/
int size = vertices.size();
// (size of Vector3 list) * (3 for each object) * (4 bytes per float)
ByteBuffer bb = ByteBuffer.allocateDirect( size * 3 * 4 );
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// Get the ByteBuffer as a floatBuffer
verticesBuffer = bb.asFloatBuffer();
for(int i = 0; i < size; i++)
verticesBuffer.put(vertices.get(i).toArray());
verticesBufferNeedsUpdate = false;
}
verticesBuffer.position(0);
return verticesBuffer;
}
public ShortBuffer getFacesBuffer(){
if(facesBuffer == null || facesBufferNeedsUpdate){
/*
* Cache faces
*/
int size = faces.size();
// Log.i(TAG, "FACES Size: "+size);
// (size of Vector3 list) * (3 for each object) * (2 bytes per short)
ByteBuffer bb = ByteBuffer.allocateDirect( size * 3 * 2 );
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// Get the ByteBuffer as a floatBuffer
facesBuffer = bb.asShortBuffer();
for(int i = 0; i < size; i++)
facesBuffer.put(faces.get(i).toArray());
facesBufferNeedsUpdate = false;
}
facesBuffer.position(0);
return facesBuffer;
}
}
Also, The Mesh class, responsable for resndering Geometry objects:
public class Mesh extends Object3D{
[...]
public void draw(float[] projectionMatrix, int shaderProgram){
float[] MVMatrix = getMVMatrix();
Matrix.multiplyMM(projectionMatrix, 0, projectionMatrix, 0, MVMatrix, 0);
// Check if geometry is set
if(geometry == null){
Log.i(TAG, "Geometry is null. skiping");
return;
}
// Add program to OpenGL environment
GLES20.glUseProgram(shaderProgram);
// Get, enable and Set the position attribute
positionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
// Prepare the triangles coordinate data
Buffer vertexBuffer = geometry.getVerticesBuffer();
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
COORDS_PER_VERTEX*4,
vertexBuffer);
// get handle to fragment shader's vColor member
int mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color.toArray(), 0);
// get handle to shape's transformation matrix
int mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
ChwaziSurfaceView.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, projectionMatrix, 0);
ChwaziSurfaceView.checkGlError("glUniformMatrix4fv");
// Draw the triangles
if(geometry.triangleType == GLES20.GL_TRIANGLES){
Buffer indexesBuffer = geometry.getFacesBuffer();
GLES20.glDrawElements(
GLES20.GL_TRIANGLES,
geometry.faces.size()*3,
GL10.GL_UNSIGNED_SHORT,
indexesBuffer);
}else{
GLES20.glDrawArrays(geometry.triangleType, 0, geometry.vertices.size());
ChwaziSurfaceView.checkGlError("glDrawArrays");
}
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
}
}
This is the sample code I made to test if it's working properly (just translation)
// Inside my Renderer...
#Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0,
0, 0, -3,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Create a rotation for the triangle
long time = SystemClock.uptimeMillis();// % 4000L;
myMesh.position.x = (time%4000)/4000f;
myMesh.draw(mMVPMatrix, shaderProgram.getProgram());
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
Matrix.orthoM(mProjMatrix, 0, -1, 1, -1, 1, 0, 10);
}
EDIT
Shader code:
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
I'm trying to display simple mesh, but I've got the following problem:
when I'm trying to translate mesh, or specifying most of perspective matrix parameters (using the Matrix.frustumM method), my mesh is getting distorted. It shrinks by the Z axis, and stretches by the Y axis.
My camera matrix code:
Matrix.setLookAtM(mViewMatrix, 0, 0, 0,-3, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
My projection matrix code (copy-pasted from here: source )
float near = 1.0f;
float far = 10.0f;
float ratio = (float) width/height;
float fov = 60;
float top = (float)Math.tan(fov * Math.PI / 360.0f) * near;
float bottom = -top;
float left = ratio * bottom;
float right = ratio * top;
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
My vertex shader code:
private static String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aPosition;" +
"attribute vec2 aTextureCoordIn;"+
"varying vec2 vTextureCoordOut;"+
"void main() {"+
" vTextureCoordOut = aTextureCoordIn;"+
" gl_Position = aPosition * uMVPMatrix;" +
"}";
Mesh is scaled using the 0.2 multiplier, using the following code:
mModelMatrix = new float[16];
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotateX, 1, 0, 0);
Matrix.rotateM(mModelMatrix, 0, rotateY, 0, 1, 0);
Matrix.rotateM(mModelMatrix, 0, rotateZ, 0, 0, 1);
Matrix.scaleM(mModelMatrix, 0, scaleX, scaleY, scaleZ);
Matrix.translateM(mModelMatrix, 0, translateX, translateY, translateZ);
And mvp matrix multiplied using this:
float[] mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
I have tried solutions from the following topics:first,second, but without success.
Screenshots:
I have also found that when I'm using the following projection matrix, and dont translate mesh, it looks correct:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 6);
Screen:
I've tried to find solution for several hours, but without any effect. I'm sure that the problem with my matrices, but I dont know how to resolve it.
Any ideas are highly appreciated.
Well, there is a huge confusion with android tutorials.. The way you multiply your matrices dictates to multiply the position in the vertex shader main function the same way, thus:
instead of:
"gl_Position = aPosition * uMVPMatrix;" +
do:
"gl_Position = uMVPMatrix * aPosition;" +
I've been making an Android OpenGLES2.0 2D game engine for the past week or so, and after a few bumps in the road, I've largely been successful. I've got the ModelMatrix, ProjectionMatrix, ViewMatrix, LightMatrix, shaders, 2D planes, and textures implemented. However, although my data is seemingly passing through this jungle of pipeline just fine, my textures do not appear, and are instead a solid black.
Most, if not all of my code was derived from this source, and it is ultimately the same, except that I created my own shader class, bounding box class, room class, and game object class to simplify the process of instantiating objects in-game. Renderer takes Room, Room takes GameObject(s) (SpaceShip extends game object), and GameObject takes BoundingBox, then Renderer renders the room's objects in a for loop. To do this, I moved the exact code from the example around so that certain handles are elements of some of the classes I created, instead of being elements of the renderer. This hasn't caused any problems with matrix multiplication or my data reaching the end of the pipeline, so I doubt moving the handles is the problem, but I felt it was important to know.
Things I've tried:
Changing the bitmap
Changed it to a bitmap with no alpha channel, both were 32x32 (2^5) and were .png.
Changing the order of operations
I moved glBindTexture in my implementation, so I moved it back, then back again.
Changing the texture parameters
I tried several combinations, none with mip-mapping
Changing the way I load the image
Went from BitmapFactory.decodeResource to BitmapFactory.decodeStream
Moved the texture to all drawable folders
Also tried it in the raw folder
Tried it on another device
My friend's DROID (Froyo 2.2), My rooted NextBook (Gingerbread 2.3). Both support OpenGLES2.0.
Thigs I haven't tried (That I'm aware of):
Changing the texture coordinates
They came directly from the example. I just took one face of the cube.
Changing my shader
It also came directly from the example (aside from it being it's own class now).
Restructuring my program to be just two (3, 4... x) classes
Dude...
I've been testing on the emulator (Eclipse Indigo, AVD, Intel Atom x86, ICS 4.2.2, API level 17) for some time now, and right about the time I got all the matrixes working, the emulator failed to render anything. It used to render just fine (when the projection was all screwy), now it just shows up black with a titlebar. This has made debugging incredibly difficult. I'm not sure if this is something related to what I've done (probably is) or if it is related to the emulator sucking at OpenGL.
Sorry to be so long winded and include so much code, but I don't know how to use a show/hide button.
Any ideas?
Edit: I was using the wrong shader from the example. The naming was very misleading. I wasn't passing in the color info. I still don't have texture, but the emulator works again. :)
OpenGLES20_2DRenderer
package mycompany.OpenGLES20_2DEngine;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
public class OpenGLES20_2DRenderer implements GLSurfaceView.Renderer {
/** Used for debug logs. */
private static final String TAG = "Renderer";
//Matrix Declarations*************************
/**
* Store the model matrix. This matrix is used to move models from object space (where each model can be thought
* of being located at the center of the universe) to world space.
*/
private float[] mModelMatrix = new float[16];
/**
* Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
* it positions things relative to our eye.
*/
private float[] mViewMatrix = new float[16];
/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
private float[] mProjectionMatrix = new float[16];
/** Allocate storage for the final combined matrix. This will be passed into the shader program. */
private float[] mMVPMatrix = new float[16];
/**
* Stores a copy of the model matrix specifically for the light position.
*/
private float[] mLightModelMatrix = new float[16];
//********************************************
//Global Variable Declarations****************
//Shader
Shader shader;
//PointShader
PointShader pointShader;
//Application Context
Context context;
//A room to add objects to
Room room;
//********************************************
public OpenGLES20_2DRenderer(Context ctx) {
context = ctx;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
//Initialize GLES20***************************
// Set the background frame color
GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
// Use culling to remove back faces.
GLES20.glEnable(GLES20.GL_CULL_FACE);
// Enable depth testing
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// Position the eye in front of the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = -0.5f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = -5.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
// Set the view matrix. This matrix can be said to represent the camera position.
// NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
// view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
//********************************************
//Initialize Shaders**************************
shader = new Shader();
pointShader = new PointShader();
//********************************************
//Load The Level******************************
//Create a new room
room = new Room(800,600, 0);
//Load game objects
SpaceShip user = new SpaceShip();
//Load sprites
for(int i=0;i<room.numberOfGameObjects;i++) {
room.gameObjects[i].spriteGLIndex = room.gameObjects[i].loadSprite(context, room.gameObjects[i].spriteResId);
}
//Add them to the room
room.addGameObject(user);
//********************************************
}
public void onDrawFrame(GL10 unused) {
//Caclulate MVPMatrix*************************
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set our per-vertex lighting program.
GLES20.glUseProgram(shader.mProgram);
// Set program handles for object drawing.
shader.mMVPMatrixHandle = GLES20.glGetUniformLocation(shader.mProgram, "u_MVPMatrix");
shader.mMVMatrixHandle = GLES20.glGetUniformLocation(shader.mProgram, "u_MVMatrix");
shader.mLightPosHandle = GLES20.glGetUniformLocation(shader.mProgram, "u_LightPos");
shader.mTextureUniformHandle = GLES20.glGetUniformLocation(shader.mProgram, "u_Texture");
shader.mPositionHandle = GLES20.glGetAttribLocation(shader.mProgram, "a_Position");
shader.mColorHandle = GLES20.glGetAttribLocation(shader.mProgram, "a_Color");
shader.mNormalHandle = GLES20.glGetAttribLocation(shader.mProgram, "a_Normal");
shader.mTextureCoordinateHandle = GLES20.glGetAttribLocation(shader.mProgram, "a_TexCoordinate");
// Calculate position of the light. Rotate and then push into the distance.
Matrix.setIdentityM(mLightModelMatrix, 0);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -5.0f);
Matrix.rotateM(mLightModelMatrix, 0, 0, 0.0f, 1.0f, 0.0f);
Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 2.0f);
Matrix.multiplyMV(shader.mLightPosInWorldSpace, 0, mLightModelMatrix, 0, shader.mLightPosInModelSpace, 0);
Matrix.multiplyMV(shader.mLightPosInEyeSpace, 0, mViewMatrix, 0, shader.mLightPosInWorldSpace, 0);
//********************************************
//Draw****************************************
//Draw the background
//room.drawBackground(mMVPMatrix);
// Draw game objects
for(int i=0;i<room.numberOfGameObjects;i++) {
// Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, room.gameObjects[i].spriteGLIndex);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(shader.mTextureUniformHandle, 0);
//Set up the model matrix
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 4.0f, 0.0f, -7.0f);
Matrix.rotateM(mModelMatrix, 0, room.gameObjects[i].rotation, 1.0f, 0.0f, 0.0f);
//Draw the object
room.gameObjects[i].draw(mModelMatrix, mViewMatrix, mProjectionMatrix, mMVPMatrix, shader);
}
//********************************************
// Draw a point to indicate the light.********
drawLight();
//********************************************
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
//Initialize Projection Matrix****************
// Set the OpenGL viewport to the same size as the surface.
GLES20.glViewport(0, 0, width, height);
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
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;
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
//********************************************
}
// Draws a point representing the position of the light.
private void drawLight()
{
GLES20.glUseProgram(pointShader.mProgram);
final int pointMVPMatrixHandle = GLES20.glGetUniformLocation(pointShader.mProgram, "u_MVPMatrix");
final int pointPositionHandle = GLES20.glGetAttribLocation(pointShader.mProgram, "a_Position");
// Pass in the position.
GLES20.glVertexAttrib3f(pointPositionHandle, shader.mLightPosInModelSpace[0], shader.mLightPosInModelSpace[1], shader.mLightPosInModelSpace[2]);
// Since we are not using a buffer object, disable vertex arrays for this attribute.
GLES20.glDisableVertexAttribArray(pointPositionHandle);
// Pass in the transformation matrix.
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mLightModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(pointMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the point.
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
}
}
Shader
package mycompany.OpenGLES20_2DEngine;
import android.opengl.GLES20;
import android.util.Log;
public class Shader {
/** Used for debug logs. */
private static final String TAG = "Shader";
//Shaders*************************************
public int vertexShader;
public int fragmentShader;
//********************************************
//Handles*************************************
/** This will be used to pass in model position information. */
public int mPositionHandle;
/** This will be used to pass in model color information. */
public int mColorHandle;
/** This will be used to pass in model normal information. */
public int mNormalHandle;
/** This will be used to pass in model texture coordinate information. */
public int mTextureCoordinateHandle;
/** This will be used to pass in the transformation matrix. */
public int mMVPMatrixHandle;
/** This will be used to pass in the modelview matrix. */
public int mMVMatrixHandle;
/** This will be used to pass in the light position. */
public int mLightPosHandle;
/** This will be used to pass in the texture. */
public int mTextureUniformHandle;
/** Used to hold a light centered on the origin in model space. We need a 4th coordinate so we can get translations to work when
* we multiply this by our transformation matrices. */
public final float[] mLightPosInModelSpace = new float[] {0.0f, 0.0f, 0.0f, 1.0f};
/** Used to hold the current position of the light in world space (after transformation via model matrix). */
public final float[] mLightPosInWorldSpace = new float[4];
/** Used to hold the transformed position of the light in eye space (after transformation via modelview matrix) */
public final float[] mLightPosInEyeSpace = new float[4];
//********************************************
//GL Code For Shaders*************************
public final String vertexShaderCode =
// A constant representing the combined model/view/projection matrix.
"uniform mat4 u_MVPMatrix;" + "\n" +
// A constant representing the combined model/view matrix.
"uniform mat4 u_MVMatrix;" + "\n" +
// Per-vertex position information we will pass in.
"attribute vec4 a_Position;" + "\n" +
// Per-vertex normal information we will pass in.
"attribute vec3 a_Normal;" + "\n" +
// Per-vertex texture coordinate information we will pass in.
"attribute vec2 a_TexCoordinate;" + "\n" +
// This will be passed into the fragment shader.
"varying vec3 v_Position;" + "\n" +
// This will be passed into the fragment shader.
"varying vec3 v_Normal;" + "\n" +
// This will be passed into the fragment shader.
"varying vec2 v_TexCoordinate;" + "\n" +
// The entry point for our vertex shader.
"void main()" + "\n" +
"{" + "\n" +
// Transform the vertex into eye space.
"v_Position = vec3(u_MVMatrix * a_Position);" + "\n" +
// Pass through the texture coordinate.
"v_TexCoordinate = a_TexCoordinate;" + "\n" +
// Transform the normal's orientation into eye space.
"v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));" + "\n" +
// gl_Position is a special variable used to store the final position.
// Multiply the vertex by the matrix to get the final point in normalized screen coordinates.
"gl_Position = u_MVPMatrix * a_Position;" + "\n" +
"}";
public final String fragmentShaderCode =
"precision mediump float;" + "\n" + // Set the default precision to medium. We don't need as high of a
// precision in the fragment shader.
"uniform vec3 u_LightPos;" + "\n" + // The position of the light in eye space.
"uniform sampler2D u_Texture;" + "\n" + // The input texture.
"varying vec3 v_Position;" + "\n" + // Interpolated position for this fragment.
"varying vec3 v_Normal;" + "\n" + // Interpolated normal for this fragment.
"varying vec2 v_TexCoordinate;" + "\n" + // Interpolated texture coordinate per fragment.
// The entry point for our fragment shader.
"void main()" + "\n" +
"{" + "\n" +
// Will be used for attenuation.
"float distance = length(u_LightPos - v_Position);" + "\n" +
// Get a lighting direction vector from the light to the vertex.
"vec3 lightVector = normalize(u_LightPos - v_Position);" + "\n" +
// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
// pointing in the same direction then it will get max illumination.
"float diffuse = max(dot(v_Normal, lightVector), 0.0);" + "\n" +
// Add attenuation.
"diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance)));" + "\n" +
// Add ambient lighting
"diffuse = diffuse + 0.7;" + "\n" +
// Multiply the color by the diffuse illumination level and texture value to get final output color.
"gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));" + "\n" +
"}";
//********************************************
//GL Program Handle***************************
public int mProgram;
//********************************************
public Shader() {
//Load Shaders********************************
vertexShader = compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
fragmentShader = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
//********************************************
//Create GL Program***************************
mProgram = createAndLinkProgram(vertexShader, fragmentShader, new String[] {"a_Position", "a_Color", "a_Normal", "a_TexCoordinate"});
//********************************************
}
/**
* Helper function to compile a shader.
*
* #param shaderType The shader type.
* #param shaderSource The shader source code.
* #return An OpenGL handle to the shader.
*/
public static int compileShader(final int shaderType, final String shaderSource)
{
int shaderHandle = GLES20.glCreateShader(shaderType);
if (shaderHandle != 0)
{
// Pass in the shader source.
GLES20.glShaderSource(shaderHandle, shaderSource);
// Compile the shader.
GLES20.glCompileShader(shaderHandle);
// Get the compilation status.
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
// If the compilation failed, delete the shader.
if (compileStatus[0] == 0)
{
Log.e(TAG, "Error compiling shader " /*+ GLES20.glGetShaderInfoLog(shaderHandle)*/);
GLES20.glDeleteShader(shaderHandle);
shaderHandle = 0;
}
}
if (shaderHandle == 0)
{
throw new RuntimeException("Error creating shader.");
}
return shaderHandle;
}
/**
* Helper function to compile and link a program.
*
* #param vertexShaderHandle An OpenGL handle to an already-compiled vertex shader.
* #param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader.
* #param attributes Attributes that need to be bound to the program.
* #return An OpenGL handle to the program.
*/
public static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes)
{
int programHandle = GLES20.glCreateProgram();
if (programHandle != 0)
{
// Bind the vertex shader to the program.
GLES20.glAttachShader(programHandle, vertexShaderHandle);
// Bind the fragment shader to the program.
GLES20.glAttachShader(programHandle, fragmentShaderHandle);
// Bind attributes
if (attributes != null)
{
final int size = attributes.length;
for (int i = 0; i < size; i++)
{
GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
}
}
// Link the two shaders together into a program.
GLES20.glLinkProgram(programHandle);
// Get the link status.
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
// If the link failed, delete the program.
if (linkStatus[0] == 0)
{
Log.e(TAG, "Error compiling program " /*+ GLES20.glGetProgramInfoLog(programHandle)*/);
GLES20.glDeleteProgram(programHandle);
programHandle = 0;
}
}
if (programHandle == 0)
{
throw new RuntimeException("Error creating program.");
}
return programHandle;
}
}
GameObject
package mycompany.OpenGLES20_2DEngine;
import java.io.IOException;
import java.io.InputStream;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.util.Log;
public class GameObject {
/** Used for debug logs. */
private static final String TAG = "GameObject";
//Declare Variables****************************
//Position
public int x;
public int y;
public int z;
//Size
public int width;
public int height;
//Movement
double thrustX;
double thrustY;
//Rotation
public int rotation;
public int rotationSpeed;
//Unique Identifier
public int UID;
//Sprite Resource ID
int spriteResId;
//GL Texture Reference
int spriteGLIndex;
//Bounding Box
BoundingBox boundingBox;
//********************************************
GameObject() {
}
public int loadSprite(final Context context, final int resourceId) {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
InputStream is = context.getResources()
.openRawResource(resourceId);
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch(IOException e) {
Log.e(TAG, "Could not load the texture");
}
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
//TODO: Offending Line - Makes textures black because of parameters
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
public void setUID(int uid) {
UID = uid;
}
public int getUID() {
return UID;
}
public void draw(float[] mModelMatrix, float[] mViewMatrix, float[] mProjectionMatrix, float[] mMVPMatrix, Shader shader) {
{
// Pass in the position information
boundingBox.mPositions.position(0);
GLES20.glVertexAttribPointer(shader.mPositionHandle, boundingBox.mPositionDataSize, GLES20.GL_FLOAT, false,
0, boundingBox.mPositions);
GLES20.glEnableVertexAttribArray(shader.mPositionHandle);
// Pass in the color information
boundingBox.mColors.position(0);
GLES20.glVertexAttribPointer(shader.mColorHandle, boundingBox.mColorDataSize, GLES20.GL_FLOAT, false,
0, boundingBox.mColors);
GLES20.glEnableVertexAttribArray(shader.mColorHandle);
// Pass in the normal information
boundingBox.mNormals.position(0);
GLES20.glVertexAttribPointer(shader.mNormalHandle, boundingBox.mNormalDataSize, GLES20.GL_FLOAT, false,
0, boundingBox.mNormals);
GLES20.glEnableVertexAttribArray(shader.mNormalHandle);
// Pass in the texture coordinate information
boundingBox.mTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(shader.mTextureCoordinateHandle, boundingBox.mTextureCoordinateDataSize, GLES20.GL_FLOAT, false,
0, boundingBox.mTextureCoordinates);
GLES20.glEnableVertexAttribArray(shader.mTextureCoordinateHandle);
// This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
// (which currently contains model * view).
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
// Pass in the modelview matrix.
GLES20.glUniformMatrix4fv(shader.mMVMatrixHandle, 1, false, mMVPMatrix, 0);
// This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
// (which now contains model * view * projection).
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
// Pass in the combined matrix.
GLES20.glUniformMatrix4fv(shader.mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Pass in the light position in eye space.
GLES20.glUniform3f(shader.mLightPosHandle, shader.mLightPosInEyeSpace[0], shader.mLightPosInEyeSpace[1], shader.mLightPosInEyeSpace[2]);
// Draw the object
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
}
}
BoundingBox
package mycompany.OpenGLES20_2DEngine;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
//TODO: make this dynamic, both the constructor and the coordinates.
class BoundingBox {
//Variable Declarations***********************
/** How many bytes per float. */
private final int mBytesPerFloat = 4;
/** Store our model data in a float buffer. */
public final FloatBuffer mPositions;
public final FloatBuffer mColors;
public final FloatBuffer mNormals;
public final FloatBuffer mTextureCoordinates;
//Number of coordinates per vertex in this array
final int COORDS_PER_VERTEX = 3;
//Coordinates
float[] positionData;
//Texture Coordinates
float[] textureCoordinateData;
//Vertex Color
float[] colorData;
float[] normalData;
//Vertex Stride
final int vertexStride = COORDS_PER_VERTEX * 4;
/** Size of the position data in elements. */
public final int mPositionDataSize = 3;
/** Size of the color data in elements. */
public final int mColorDataSize = 4;
/** Size of the normal data in elements. */
public final int mNormalDataSize = 3;
/** Size of the texture coordinate data in elements. */
public final int mTextureCoordinateDataSize = 2;
//********************************************
public BoundingBox(float[] coords) {
//TODO: Normalize values
//Set Coordinates and Texture Coordinates*****
if(coords==null) {
float[] newPositionData = {
// Front face
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f
};
positionData = newPositionData;
float[] newColorData = {
// Front face (red)
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f
};
colorData = newColorData;
float[] newTextureCoordinateData =
{
// Front face
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
};
textureCoordinateData = newTextureCoordinateData;
float[] newNormalData = {
// Front face
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f
};
normalData = newNormalData;
}
else {
positionData = coords;
//TODO:Reverse coords HERE
textureCoordinateData = coords;
}
//********************************************
//Initialize Buffers**************************
mPositions = ByteBuffer.allocateDirect(positionData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mPositions.put(positionData).position(0);
mColors = ByteBuffer.allocateDirect(colorData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mColors.put(colorData).position(0);
mNormals = ByteBuffer.allocateDirect(normalData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mNormals.put(normalData).position(0);
mTextureCoordinates = ByteBuffer.allocateDirect(textureCoordinateData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTextureCoordinates.put(textureCoordinateData).position(0);
//********************************************
}
}
SpaceShip
package mycompany.OpenGLES20_2DEngine;
public class SpaceShip extends GameObject{
public SpaceShip() {
spriteResId = R.drawable.spaceship;
boundingBox = new BoundingBox(null);
}
}
Got it. I added the spaceship to the room AFTER I loaded it's bitmap (from the room).
//Load The Level******************************
//Create a new room
room = new Room(800,600, 0);
//Load game objects
SpaceShip user = new SpaceShip();
**//Load sprites
for(int i=0;i<room.numberOfGameObjects;i++) {
room.gameObjects[i].spriteGLIndex = room.gameObjects[i].loadSprite(context, room.gameObjects[i].spriteResId);
}
//Add them to the room
room.addGameObject(user);**
//********************************************