package com.homelinux.holtrop.opengltest; import android.opengl.GLSurfaceView; import android.opengl.GLES20; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.egl.EGLConfig; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ByteOrder; import android.util.Log; import android.content.res.AssetManager; import android.content.res.Resources; import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import android.opengl.Matrix; import android.os.SystemClock; import android.view.MotionEvent; import android.graphics.BitmapFactory; import android.graphics.Bitmap; import android.opengl.GLUtils; import java.lang.Math; public class MyRenderer implements GLSurfaceView.Renderer { class Tile { public boolean flipping; public boolean side; public float rotation; public int axis; }; private int m_program; private final String DBGTAG = "JoshsOpenGL"; private FloatBuffer m_quad_attrib_buffer; private AssetManager m_asset_manager; private Resources m_resources; private float m_proj_matrix[] = new float[16]; private float m_modelview[] = new float[16]; private int m_width; private int m_height; private final int GRID_WIDTH = 16; private final int GRID_HEIGHT = 9; private Tile[][] m_tiles = new Tile[GRID_WIDTH][GRID_HEIGHT]; private float m_aspect = 1.0f; private final float DISTANCE = (float) (GRID_HEIGHT / 2.0 / Math.tan(Math.toRadians(30))); private long m_last_time = SystemClock.uptimeMillis(); private final float[][] AXES = { {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, {0, -1, 0} }; public MyRenderer(AssetManager am, Resources resources) { m_asset_manager = am; m_resources = resources; for (int x = 0; x < GRID_WIDTH; x++) { for (int y = 0; y < GRID_HEIGHT; y++) { m_tiles[x][y] = new Tile(); } } } public void checkGLError(String glOperation) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(DBGTAG, glOperation + ": glError " + error); } } private String read_asset(String fname) { String result = ""; try { InputStream is = m_asset_manager.open(fname); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { result += line + "\n"; } } catch (IOException ioe) { } return result; } private int load_shader(int type, String source) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); IntBuffer ib = IntBuffer.allocate(1); GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, ib); if (ib.get(0) != 1) { String info_log = GLES20.glGetShaderInfoLog(shader); Log.e(DBGTAG, "Error compiling shader: " + info_log); } return shader; } public void onSurfaceCreated(GL10 unused, EGLConfig config) { final float attribs[] = { 0.5f, 0.5f, 0.8f, 0.8f, 1, -0.5f, 0.5f, 0, 0, 1, -0.5f, -0.5f, 0.8f, 0.8f, 1, 0.5f, -0.5f, 0, 0, 1 }; checkGLError("onSurfaceCreated"); ByteBuffer bb = ByteBuffer.allocateDirect(attribs.length * 4); bb.order(ByteOrder.nativeOrder()); m_quad_attrib_buffer = bb.asFloatBuffer(); m_quad_attrib_buffer.put(attribs); m_quad_attrib_buffer.position(0); final String v_shader_src = read_asset("shaders/vertex.glsl"); final String f_shader_src = read_asset("shaders/fragment.glsl"); int v_shader = load_shader(GLES20.GL_VERTEX_SHADER, v_shader_src); int f_shader = load_shader(GLES20.GL_FRAGMENT_SHADER, f_shader_src); m_program = GLES20.glCreateProgram(); GLES20.glAttachShader(m_program, v_shader); GLES20.glAttachShader(m_program, f_shader); GLES20.glLinkProgram(m_program); checkGLError("glLinkProgram"); IntBuffer ib = IntBuffer.allocate(1); GLES20.glGetProgramiv(m_program, GLES20.GL_LINK_STATUS, ib); if (ib.get(0) != 1) { String info_log = GLES20.glGetProgramInfoLog(m_program); Log.e(DBGTAG, "Program log: " + info_log); } /* Bitmap texture = BitmapFactory.decodeResource(m_resources, R.drawable.texture); int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); checkGLError("glGenTextures"); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); checkGLError("glBindTexture"); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); checkGLError("glTexParameterf"); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, texture, 0); checkGLError("GLUtils.texImage2D"); GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); checkGLError("glGenerateMipmap"); texture.recycle(); */ GLES20.glClearColor(1.0f, 0.6f, 0.1f, 1.0f); } private void update_tiles(long elapsed) { for (int x = 0; x < GRID_WIDTH; x++) { for (int y = 0; y < GRID_HEIGHT; y++) { Tile t = m_tiles[x][y]; if (t.flipping) { t.rotation += elapsed * (180.0f / 2000.0f); if (t.rotation >= 180.0f) { t.flipping = false; t.side = !t.side; } } } } } public void onDrawFrame(GL10 unused) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); long time = SystemClock.uptimeMillis(); update_tiles(time - m_last_time); int attr_pos = GLES20.glGetAttribLocation(m_program, "pos"); int attr_color = GLES20.glGetAttribLocation(m_program, "color"); checkGLError("glGetAttribLocation"); GLES20.glEnableVertexAttribArray(attr_pos); GLES20.glEnableVertexAttribArray(attr_color); checkGLError("glEnableVertexAttribArray"); m_quad_attrib_buffer.position(0); GLES20.glVertexAttribPointer(attr_pos, 2, GLES20.GL_FLOAT, false, 5 * 4, m_quad_attrib_buffer); m_quad_attrib_buffer.position(2); GLES20.glVertexAttribPointer(attr_color, 3, GLES20.GL_FLOAT, false, 5 * 4, m_quad_attrib_buffer); checkGLError("glVertexAttribPointer"); for (int y = 0; y < GRID_HEIGHT; y++) { for (int x = 0; x < GRID_WIDTH; x++) { Tile t = m_tiles[x][y]; Matrix.setIdentityM(m_modelview, 0); Matrix.translateM(m_modelview, 0, x + 0.5f - GRID_WIDTH / 2.0f, y + 0.5f - GRID_HEIGHT / 2.0f, -DISTANCE); if (t.flipping) { Matrix.rotateM(m_modelview, 0, t.rotation, AXES[t.axis][0], AXES[t.axis][1], AXES[t.axis][2]); } GLES20.glUniformMatrix4fv( GLES20.glGetUniformLocation(m_program, "modelview"), 1, false, m_modelview, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4); checkGLError("glDrawArrays"); } } GLES20.glDisableVertexAttribArray(attr_pos); GLES20.glDisableVertexAttribArray(attr_color); checkGLError("glDisableVertexAttribArray"); m_last_time = time; } public void onSurfaceChanged(GL10 unused, int width, int height) { m_width = width; m_height = height; m_aspect = width / (float) height; GLES20.glViewport(0, 0, width, height); Matrix.perspectiveM(m_proj_matrix, 0, 60.0f, 16.0f/9.0f, 0.1f, 10.0f); GLES20.glUseProgram(m_program); GLES20.glUniformMatrix4fv( GLES20.glGetUniformLocation(m_program, "projection"), 1, false, m_proj_matrix, 0); } public boolean onTouchEvent(MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) { int x = (int) (GRID_WIDTH * e.getX() / (float) m_width); int y = (int) (GRID_HEIGHT * (m_height - e.getY()) / (float) m_height); if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) { Tile t = m_tiles[x][y]; if (!t.flipping) { t.flipping = true; t.rotation = 0.0f; t.axis = (int) (Math.random() * 4); } } return true; } return false; } }