package edu.cs4730.opengl30cubetextureview;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.opengl.GLUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.TextureView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
/*
* This code is borrowed from https://github.com/ykulbashian/LiquidSurface/blob/master/liquidview/src/main/java/com/mycardboarddreams/liquidsurface/GLTextureView.java
* based on a lot of android and others saying how to do this. But allowing everyone to keep their
* rendereres.
*
* As note, while it is mostly ykulbashian code from https://github.com/ykulbashian/LiquidSurface repo
* , I changed the renderer to myRenderer and left in my ontouchevents.
*/
public class myGLTextureView extends TextureView implements TextureView.SurfaceTextureListener {
myRenderer mRenderer;
private static final int TARGET_FRAME_RATE = 55;
private static final int EGL_OPENGL_ES2_BIT = 4;
private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
private static final String TAG = "RenderThread";
private SurfaceTexture mSurface;
private EGLDisplay mEglDisplay;
private EGLSurface mEglSurface;
private EGLContext mEglContext;
private EGL10 mEgl;
private EGLConfig eglConfig;
private GL10 mGl;
private int targetFrameDurationMillis;
private int surfaceHeight;
private int surfaceWidth;
public boolean isRunning = false;
private boolean paused = true;
private boolean rendererChanged = false;
private RenderThread thread;
private int targetFps;
public myGLTextureView(Context context) {
super(context);
initialize(context);
}
public myGLTextureView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public myGLTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
public synchronized void setRenderer(myRenderer renderer){
mRenderer = renderer;
rendererChanged = true;
}
private void initialize(Context context) {
targetFps = TARGET_FRAME_RATE;
setSurfaceTextureListener(this);
}
//private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private static final float TOUCH_SCALE_FACTOR = 0.015f;
private float mPreviousX;
private float mPreviousY;
@Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
// and other input controls. In this case, you are only
// interested in events where the touch position changed.
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
//subtract, so the cube moves the same direction as your finger.
//with plus it moves the opposite direction.
mRenderer.setX(mRenderer.getX() - (dx * TOUCH_SCALE_FACTOR));
float dy = y - mPreviousY;
mRenderer.setY(mRenderer.getY() - (dy * TOUCH_SCALE_FACTOR));
Log.v(TAG, "moving?");
}
mPreviousX = x;
mPreviousY = y;
return true;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
startThread(surface, width, height, targetFps);
}
public void startThread(SurfaceTexture surface, int width, int height, float targetFramesPerSecond){
Log.d(TAG, "Starting GLTextureView thread");
thread = new RenderThread();
mSurface = surface;
setDimensions(width, height);
targetFrameDurationMillis = (int) ((1f/targetFramesPerSecond)*1000);
thread.start();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
setDimensions(width, height);
if(mRenderer != null)
mRenderer.onSurfaceChanged(mGl, width, height);
}
public synchronized void setPaused(boolean isPaused){
Log.d(TAG, String.format("Setting GLTextureView paused to %s", isPaused));
paused = isPaused;
}
public synchronized boolean isPaused(){
return paused;
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.e(TAG, "SurfaceTExture Destroyed call.");
stopThread();
return false;
}
public void stopThread(){
if(thread != null){
Log.d(TAG, "Stopping and joining GLTextureView");
isRunning = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread = null;
}
}
private boolean shouldSleep(){
return isPaused() || mRenderer == null;
}
private class RenderThread extends Thread {
@Override
public void run() {
isRunning = true;
initGL();
checkGlError();
long lastFrameTime = System.currentTimeMillis();
while (isRunning) {
while (mRenderer == null){
try {
Thread.sleep(100);
} catch (InterruptedException e){
// Ignore
}
}
if(rendererChanged){
initializeRenderer(mRenderer);
rendererChanged = false;
}
if (!shouldSleep()) {
lastFrameTime = System.currentTimeMillis();
drawSingleFrame();
}
try {
if (shouldSleep())
Thread.sleep(100);
else {
long thisFrameTime = System.currentTimeMillis();
long timDiff = thisFrameTime - lastFrameTime;
lastFrameTime = thisFrameTime;
Thread.sleep(Math.max(10l, targetFrameDurationMillis - timDiff));
}
} catch (InterruptedException e) {
// Ignore
}
}
}
}
private synchronized void initializeRenderer(myRenderer renderer) {
if(renderer != null && isRunning) {
//Log.v(TAG, "initialing and startin? renderer");
//renderer.mySetup(mGl30);
renderer.onSurfaceCreated(mGl, eglConfig);
renderer.onSurfaceChanged(mGl, surfaceWidth, surfaceHeight);
}
}
private synchronized void drawSingleFrame() {
checkCurrent();
if(mRenderer != null) {
mRenderer.onDrawFrame(mGl);
//Log.v(TAG, "drawing in renderer");
}
checkGlError();
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
Log.e(TAG, "cannot swap buffers!");
}
}
public void setDimensions(int width, int height){
surfaceWidth = width;
surfaceHeight = height;
}
private void checkCurrent() {
if (!mEglContext.equals(mEgl.eglGetCurrentContext())
|| !mEglSurface.equals(mEgl
.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
checkEglError();
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface,
mEglSurface, mEglContext)) {
throw new RuntimeException(
"eglMakeCurrent failed "
+ GLUtils.getEGLErrorString(mEgl
.eglGetError()));
}
checkEglError();
}
}
private void checkEglError() {
final int error = mEgl.eglGetError();
if (error != EGL10.EGL_SUCCESS) {
Log.e(TAG, "EGL error = 0x" + Integer.toHexString(error));
}
}
private void checkGlError() {
final int error = mGl.glGetError();
if (error != GL11.GL_NO_ERROR) {
Log.e(TAG, "GL error = 0x" + Integer.toHexString(error));
}
}
private void initGL() {
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed "
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
}
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed "
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
}
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
int[] configSpec = {
EGL10.EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16, //was 0
EGL10.EGL_STENCIL_SIZE, 0,
EGL10.EGL_NONE
};
eglConfig = null;
if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1,
configsCount)) {
throw new IllegalArgumentException(
"eglChooseConfig failed "
+ GLUtils.getEGLErrorString(mEgl
.eglGetError()));
} else if (configsCount[0] > 0) {
eglConfig = configs[0];
}
if (eglConfig == null) {
throw new RuntimeException("eglConfig not initialized");
}
int[] attrib_list = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE
};
mEglContext = mEgl.eglCreateContext(mEglDisplay,
eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
checkEglError();
mEglSurface = mEgl.eglCreateWindowSurface(
mEglDisplay, eglConfig, mSurface, null);
checkEglError();
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e(TAG,
"eglCreateWindowSurface returned EGL10.EGL_BAD_NATIVE_WINDOW");
return;
}
throw new RuntimeException(
"eglCreateWindowSurface failed "
+ GLUtils.getEGLErrorString(error));
}
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface,
mEglSurface, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed "
+ GLUtils.getEGLErrorString(mEgl.eglGetError()));
}
checkEglError();
mGl = (GL10) mEglContext.getGL();
checkEglError();
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}