+package com.codesrc.glboy;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+
+import android.app.Activity;
+
+import android.os.Bundle;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+import android.view.MotionEvent;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class GlBoyActivity extends Activity
+{
+ static {
+ System.loadLibrary("glBoy");
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ stop();
+ //mGLView = new GameboyView(this);
+ //setContentView(mGLView);
+
+ setContentView(R.layout.main);
+ mGLView = (GLSurfaceView) findViewById(R.id.glsurfaceview);
+ mGLView.setEGLContextClientVersion(2); // GLES 2.0
+ mGLView.setRenderer(new GameboyRenderer(this));
+
+ // Find our buttons
+ // Button aButton = (Button) findViewById(R.id.buttonA);
+
+ // Wire each button to a click listener
+ // aButton.setOnTouchListener(OnTouchListener)
+ // aButton.set.setOnClickListener(mVisibleListener);
+
+
+ }
+ /*
+ OnClickListener mInvisibleListener = new OnTouchListener() {
+ public void onClick(View v) {
+ mVictim1.setVisibility(View.INVISIBLE);
+ mVictim2.setVisibility(View.INVISIBLE);
+ mVictimContainer.setVisibility(View.INVISIBLE);
+ }
+ };*/
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+ loadCart("/sdcard/tetris.zip");
+ }
+
+ @Override
+ public void onStop()
+ {
+ super.onStop();
+ stop();
+ }
+
+ @Override
+ public void onPause()
+ {
+ super.onPause();
+ pause();
+ mGLView.onPause();
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ resume();
+ mGLView.onResume();
+ }
+
+ private native boolean loadCart(String filename);
+ private native void pause();
+ private native void resume();
+ private native void stop();
+ public native void getFrame(byte[] framebuffer);
+
+ private GLSurfaceView mGLView;
+}
+
+class GameboyView extends GLSurfaceView
+{
+ public GameboyView(GlBoyActivity context)
+ {
+ super(context);
+ setEGLContextClientVersion(2); // GLES 2.0
+ mRenderer = new GameboyRenderer(context);
+ setRenderer(mRenderer);
+
+ // setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS);
+ }
+
+ public boolean onTouchEvent(final MotionEvent event) {
+ /*
+ queueEvent(new Runnable(){
+ public void run() {
+ mRenderer.setColor(event.getX() / getWidth(),
+ event.getY() / getHeight(), 1.0f);
+ }});
+ */
+ return true;
+ }
+
+ GameboyRenderer mRenderer;
+}
+
+class GameboyRenderer implements GLSurfaceView.Renderer
+{
+ GameboyRenderer(GlBoyActivity context)
+ {
+ m_activity = context;
+ m_frameBuffer = ByteBuffer.wrap(m_frameBufferBytes);
+
+ }
+ public void onSurfaceCreated(GL10 obsolete, EGLConfig config)
+ {
+ m_program = createProgram(m_vertexShader, m_fragmentShader);
+ if (m_program == 0) {
+ return;
+ }
+
+ m_aPosition = GLES20.glGetAttribLocation(m_program, "a_position");
+ checkGlError("glGetAttribLocation a_position");
+ if (m_aPosition == -1) {
+ throw new RuntimeException("Could not get attrib location for a_position");
+ }
+
+ m_aTexCoord = GLES20.glGetAttribLocation(m_program, "a_texCoord");
+ checkGlError("glGetAttribLocation a_texCoord");
+ if (m_aTexCoord == -1) {
+ throw new RuntimeException("Could not get attrib location for a_texCoord");
+ }
+
+ m_sTexture = GLES20.glGetUniformLocation(m_program, "s_texture");
+ checkGlError("glGetUniformLocation s_texture");
+ if (m_sTexture == -1) {
+ throw new RuntimeException("Could not get uniform location for s_texture");
+ }
+
+ GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+
+ // Allocate texture
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ m_textureId = textures[0];
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureId);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // Must be called before glTexParameterf
+
+ GLES20.glTexParameterf(
+ GLES20.GL_TEXTURE_2D,
+ GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(
+ GLES20.GL_TEXTURE_2D,
+ GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+
+ // Clamp-to-edge required for non-power-of-two texture
+ GLES20.glTexParameterf(
+ GLES20.GL_TEXTURE_2D,
+ GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(
+ GLES20.GL_TEXTURE_2D,
+ GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE);
+
+ ByteBuffer byteBuf = ByteBuffer.allocateDirect(m_vertices.length * 4);
+ byteBuf.order(ByteOrder.nativeOrder());
+ m_vertexBuffer = byteBuf.asFloatBuffer();
+ m_vertexBuffer.put(m_vertices);
+ m_vertexBuffer.position(0);
+
+ ByteBuffer byteBuf2 = ByteBuffer.allocateDirect(m_textureCoords.length * 4);
+ byteBuf2.order(ByteOrder.nativeOrder());
+ m_textureBuffer = byteBuf2.asFloatBuffer();
+ m_textureBuffer.put(m_textureCoords);
+ m_textureBuffer.position(0);
+ }
+
+ public void onSurfaceChanged(GL10 obsolete, int w, int h)
+ {
+ float aspect = 144.0f/160.f;
+ if (h > w)
+ {
+ int gameHeight = (int)(w*aspect);
+ GLES20.glViewport(0, h - gameHeight, w, gameHeight);
+ }
+ else
+ {
+ int gameWidth = (int)(h/aspect);
+ GLES20.glViewport((w - gameWidth)/2, 0, gameWidth, h);
+ }
+
+ // this projection matrix is applied to object coodinates
+ // in the onDrawFrame() method
+ // Camera is at (0,0,2), looking down the -z axis,
+ // with (0,1,0) "up" vector.
+ // The near and far values supplied to glOrthof define the -distance-
+ // of the clipping frames from the camera. ie. near/far of 1, 10
+ // will only draw points with a z value between 1 and -8.
+ Matrix.orthoM(m_ProjMatrix, 0, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f);
+ m_uMVPMatrixHandle = GLES20.glGetUniformLocation(m_program, "uMVPMatrix");
+
+ // Change the "view" matrix to set everything to z = -1, so it is within
+ // our clipping planes.
+ Matrix.setLookAtM(m_VMatrix, 0, 0, 0, 2, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ // Our textures increase Y when heading down. This is the opposite
+ // of the opengl coordinates, so we'll flip it here.
+ Matrix.setIdentityM(m_MMatrix, 0);
+ Matrix.scaleM(m_MMatrix, 0, 1.0f, -1.0f, 1.0f);
+
+ // May as well MVP here, since it doesn't change.
+ Matrix.multiplyMM(m_MVPMatrix, 0, m_VMatrix, 0, m_MMatrix, 0);
+ Matrix.multiplyMM(m_MVPMatrix, 0, m_ProjMatrix, 0, m_MVPMatrix, 0);
+ }
+
+ public void onDrawFrame(GL10 obsolete)
+ {
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+ GLES20.glUseProgram(m_program);
+ //checkGlError("glUseProgram");
+
+ m_activity.getFrame(m_frameBufferBytes);
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureId);
+
+ // Create the texture image
+ GLES20.glTexImage2D(
+ GLES20.GL_TEXTURE_2D,
+ 0,
+ GLES20.GL_LUMINANCE,
+ 160,
+ 144,
+ 0,
+ GLES20.GL_LUMINANCE,
+ GLES20.GL_UNSIGNED_BYTE,
+ m_frameBuffer
+ );
+ GLES20.glUniform1i(m_sTexture, 0);
+
+ GLES20.glUniformMatrix4fv(m_uMVPMatrixHandle, 1, false, m_MVPMatrix, 0);
+
+ m_vertexBuffer.position(0);
+ GLES20.glVertexAttribPointer(
+ m_aPosition,
+ 3, // x, y, z
+ GLES20.GL_FLOAT,
+ false,
+ 0, // Tightly packed
+ m_vertexBuffer);
+ GLES20.glEnableVertexAttribArray(m_aPosition);
+
+ m_textureBuffer.position(0);
+ GLES20.glVertexAttribPointer(
+ m_aTexCoord,
+ 2, // x, y
+ GLES20.GL_FLOAT,
+ false,
+ 0, // Tightly packed
+ m_textureBuffer);
+ GLES20.glEnableVertexAttribArray(m_aTexCoord);
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+ //checkGlError("glDrawArrays");
+ }
+
+ private int loadShader(int shaderType, String source) {
+ int shader = GLES20.glCreateShader(shaderType);
+ if (shader != 0) {
+ GLES20.glShaderSource(shader, source);
+ GLES20.glCompileShader(shader);
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == 0) {
+ Log.e(TAG, "Could not compile shader " + shaderType + ":");
+ Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+ }
+
+ private int createProgram(String vertexSource, String fragmentSource) {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+ if (vertexShader == 0) {
+ return 0;
+ }
+
+ int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+ if (pixelShader == 0) {
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ if (program != 0) {
+ GLES20.glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ GLES20.glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+ }
+
+ private void checkGlError(String op) {
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(TAG, op + ": glError " + error);
+ throw new RuntimeException(op + ": glError " + error);
+ }
+ }
+
+ private GlBoyActivity m_activity;
+
+ // Native code fills this out for us, and we then display it using
+ // opengl
+ byte[] m_frameBufferBytes = new byte[144*160];
+ ByteBuffer m_frameBuffer;
+
+ private int m_textureId;
+ private int m_program;
+ private int m_aPosition;
+ private int m_aTexCoord;
+ private int m_sTexture;
+ private int m_uMVPMatrixHandle;
+ private float[] m_MVPMatrix = new float[16];
+ private float[] m_VMatrix = new float[16];
+ private float[] m_MMatrix = new float[16];
+ private float[] m_ProjMatrix = new float[16];
+
+ private FloatBuffer m_vertexBuffer;
+ private FloatBuffer m_textureBuffer; // texture coords
+ private float m_vertices[] =
+ {
+ -1.0f, 1.0f, 0, // Top Left
+ 1.0f, 1.0f, 0, // Top Right
+ -1.0f, -1.0f, 0, // Bottom Left
+ 1.0f, -1.0f, 0 // Bottom Right
+ };
+ private float m_textureCoords[] =
+ {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f
+ };
+
+ private final String m_vertexShader =
+ "uniform mat4 uMVPMatrix; \n" +
+ "attribute vec4 a_position; \n" +
+ "attribute vec2 a_texCoord; \n" +
+ "varying vec2 c5; \n" +
+ "void main(){ \n" +
+ " gl_Position = uMVPMatrix * a_position; \n" +
+ " c5 = a_texCoord; \n" +
+ "}\n";
+
+
+ private final String m_fragmentShader =
+ "precision mediump float;\n" +
+ "varying vec2 c5;\n" +
+ "uniform sampler2D s_texture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(s_texture, c5);\n" +
+ // " gl_FragColor = vec4(0.5, 0.3, 0.9, 1.0);\n" +
+ "}\n";
+ private static String TAG = "glBoy Renderer";
+}