package com.jme3.system.android;
import android.app.Activity;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
+import android.opengl.GLSurfaceView.EGLConfigChooser;
import android.view.SurfaceHolder;
+
+import com.jme3.app.AndroidHarness;
+import com.jme3.app.Application;
import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
import com.jme3.input.android.AndroidInput;
-//import com.jme3.renderer.android.OGLESRenderer;
+import com.jme3.input.controls.TouchTrigger;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.OGLESShaderRenderer;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
import com.jme3.system.SystemListener;
import com.jme3.system.Timer;
+import com.jme3.system.android.AndroidConfigChooser.ConfigType;
+
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
+
+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.opengles.GL10;
-public class OGLESContext implements JmeContext, GLSurfaceView.Renderer {
+public class OGLESContext implements JmeContext, GLSurfaceView.Renderer
+{
private static final Logger logger = Logger.getLogger(OGLESContext.class.getName());
- protected AtomicBoolean created = new AtomicBoolean(false);
- protected AppSettings settings = new AppSettings(true);
+ protected final AtomicBoolean created = new AtomicBoolean(false);
+ protected final AtomicBoolean renderable = new AtomicBoolean(false);
+ protected final AtomicBoolean needClose = new AtomicBoolean(false);
+
+ protected final AppSettings settings = new AppSettings(true);
- /* < OpenGL ES 2.0 * */
- //protected OGLESRenderer renderer;
/* >= OpenGL ES 2.0 (Android 2.2+) */
protected OGLESShaderRenderer renderer;
protected Timer timer;
protected SystemListener listener;
- protected AtomicBoolean needClose = new AtomicBoolean(false);
+
protected boolean wasActive = false;
- protected int frameRate = 0;
protected boolean autoFlush = true;
protected AndroidInput view;
+
+ private long milliStart;
+ private long milliDelta;
+ protected int frameRate = 33;
+ //protected int minFrameDuration = 1000 / frameRate; // Set a max FPS of 33
+ protected int minFrameDuration = 0; // No FPS cap
+
+ /**
+ * EGL_RENDERABLE_TYPE: EGL_OPENGL_ES_BIT = OpenGL ES 1.0 | EGL_OPENGL_ES2_BIT = OpenGL ES 2.0
+ */
+ protected int clientOpenGLESVersion = 1;
+
+ protected boolean verboseLogging = false;
+
+ final private String ESCAPE_EVENT = "TouchEscape";
+
+ public OGLESContext() { }
- public OGLESContext(){
- }
-
- public Type getType() {
+ @Override
+ public Type getType()
+ {
return Type.Display;
}
-
- public GLSurfaceView createView(Activity activity){
- view = new AndroidInput(activity);
-
- /*
- * Requesting client version from GLSurfaceView which is extended by
- * AndroidInput.
- * This is required to get OpenGL ES 2.0
- */
-
- logger.info("setEGLContextClientVersion(2)");
- view.setEGLContextClientVersion(2);
- logger.info("setEGLContextClientVersion(2) ... done.");
-
- //RGB565, Depth16
- view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+
+ /**
+ * <code>createView</code>
+ * @param activity The Android activity which is parent for the GLSurfaceView
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(Activity activity)
+ {
+ return createView(new AndroidInput(activity));
+ }
+ /**
+ * <code>createView</code>
+ * @param view The Android input which will be used as the GLSurfaceView for this context
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(AndroidInput view)
+ {
+ return createView(view, ConfigType.FASTEST, false);
+ }
+
+ /**
+ * <code>createView</code> initializes the GLSurfaceView
+ * @param view The Android input which will be used as the GLSurfaceView for this context
+ * @param configType ConfigType.FASTEST (Default) | ConfigType.LEGACY | ConfigType.BEST
+ * @param eglConfigVerboseLogging if true show all found configs
+ * @return GLSurfaceView The newly created view
+ */
+ public GLSurfaceView createView(AndroidInput view, ConfigType configType, boolean eglConfigVerboseLogging)
+ {
+ // Start to set up the view
+ this.view = view;
+ verboseLogging = eglConfigVerboseLogging;
+
+ if (configType == ConfigType.LEGACY)
+ {
+ // Hardcoded egl setup
+ clientOpenGLESVersion = 2;
+ view.setEGLContextClientVersion(2);
+ //RGB565, Depth16
+// view.setEGLConfigChooser(5, 6, 5, 0, 16, 0);
+ logger.info("ConfigType.LEGACY using RGB565");
+ view.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
+ view.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ view.setZOrderOnTop(true);
+ }
+ else
+ {
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ int[] version = new int[2];
+ if (egl.eglInitialize(display, version) == true)
+ {
+ logger.info("Display EGL Version: " + version[0] + "." + version[1]);
+ }
+
+ // Create a config chooser
+ AndroidConfigChooser configChooser = new AndroidConfigChooser(configType, eglConfigVerboseLogging);
+ // Init chooser
+ if (!configChooser.findConfig(egl, display))
+ {
+ logger.severe("Unable to find suitable EGL config");
+ }
+
+ clientOpenGLESVersion = configChooser.getClientOpenGLESVersion();
+ if (clientOpenGLESVersion < 2)
+ {
+ logger.severe("OpenGL ES 2.0 is not supported on this device");
+ }
+
+ if (display != null)
+ egl.eglTerminate(display);
+
+
+ /*
+ * Requesting client version from GLSurfaceView which is extended by
+ * AndroidInput.
+ */
+ view.setEGLContextClientVersion(clientOpenGLESVersion);
+// view.setEGLConfigChooser(configChooser);
+// view.getHolder().setFormat(configChooser.getPixelFormat());
+ view.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
+ view.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ view.setZOrderOnTop(true);
+ }
+
view.setFocusableInTouchMode(true);
view.setFocusable(true);
- view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
-// view.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);
-// | GLSurfaceView.DEBUG_LOG_GL_CALLS);
- view.setRenderer(this);
- return view;
-
- }
+ view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ view.setRenderer(this);
- protected void applySettings(AppSettings setting){
+ return view;
}
-
- protected void initInThread(GL10 gl){
- logger.info("Display created.");
- logger.fine("Running on thread: "+Thread.currentThread().getName());
-
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- public void uncaughtException(Thread thread, Throwable thrown) {
- listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+
+ // renderer:initialize
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig cfg)
+ {
+
+ if (created.get() && renderer != null)
+ {
+ renderer.resetGLObjects();
+ }
+ else
+ {
+ if (!created.get())
+ {
+ logger.info("GL Surface created, doing JME3 init");
+ initInThread();
}
- });
-
- created.set(true);
+ else
+ {
+ logger.warning("GL Surface already created");
+ }
+ }
+ }
+
+ protected void initInThread()
+ {
+ created.set(true);
+
+ logger.info("OGLESContext create");
+ logger.info("Running on thread: "+Thread.currentThread().getName());
+ final Context ctx = this.view.getContext();
+
+ // Setup unhandled Exception Handler
+ if (ctx instanceof AndroidHarness)
+ {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ ((AndroidHarness)ctx).handleError("Exception thrown in " + thread.toString(), thrown);
+ }
+ });
+ }
+ else
+ {
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread thread, Throwable thrown) {
+ listener.handleError("Exception thrown in " + thread.toString(), thrown);
+ }
+ });
+ }
+
+ if (clientOpenGLESVersion < 2)
+ {
+ throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
+ }
+
timer = new AndroidTimer();
- renderer = new OGLESShaderRenderer(gl);
- applySettingsToRenderer(renderer, settings);
-
+ renderer = new OGLESShaderRenderer();
+
+// renderer.setUseVA(true);
+ renderer.setVerboseLogging(verboseLogging);
+
renderer.initialize();
listener.initialize();
-
- // OGLESShaderRenderer does not support guiView yet
- // forcefully remove all gui nodes
-
- if (listener instanceof com.jme3.app.SimpleApplication) {
- ((com.jme3.app.SimpleApplication) listener).getGuiNode().detachAllChildren();
- }
+
+ // Setup exit hook
+ if (ctx instanceof AndroidHarness)
+ {
+ Application app = ((AndroidHarness)ctx).getJmeApplication();
+ if (app.getInputManager() != null)
+ {
+ app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK));
+ app.getInputManager().addListener((AndroidHarness)ctx, new String[]{ESCAPE_EVENT});
+ }
+ }
+
+ needClose.set(false);
+ renderable.set(true);
}
/**
* De-initialize in the OpenGL thread.
*/
- protected void deinitInThread(){
- listener.destroy();
- if (renderer != null) {
- renderer.cleanup();
- // do android specific cleaning here
-
- logger.info("Display destroyed.");
- created.set(false);
- renderer = null;
- timer = null;
- }
+ protected void deinitInThread()
+ {
+ if (renderable.get())
+ {
+ created.set(false);
+ if (renderer != null)
+ renderer.cleanup();
+
+ listener.destroy();
+
+ listener = null;
+ renderer = null;
+ timer = null;
+
+ // do android specific cleaning here
+ logger.info("Display destroyed.");
+
+ renderable.set(false);
+ }
+ }
+
+ protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings)
+ {
+ logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
+ logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
+ renderer.setUseVA(settings.getBoolean("USE_VA"));
+ renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
+ }
+
+ protected void applySettings(AppSettings settings)
+ {
+ setSettings(settings);
+ if (renderer != null)
+ applySettingsToRenderer(renderer, this.settings);
}
-
- protected void applySettingsToRenderer(OGLESShaderRenderer renderer, AppSettings settings) {
- logger.warning("setSettings.USE_VA: [" + settings.getBoolean("USE_VA") + "]");
- logger.warning("setSettings.VERBOSE_LOGGING: [" + settings.getBoolean("VERBOSE_LOGGING") + "]");
- renderer.setUseVA(settings.getBoolean("USE_VA"));
- renderer.setVerboseLogging(settings.getBoolean("VERBOSE_LOGGING"));
- }
-
- @Override
- public void setSettings(AppSettings settings) {
- this.settings.copyFrom(settings);
-
- // XXX This code should be somewhere else
- if (renderer != null)
- applySettingsToRenderer(renderer, this.settings);
+ @Override
+ public void setSettings(AppSettings settings)
+ {
+ this.settings.copyFrom(settings);
}
+ @Override
public void setSystemListener(SystemListener listener){
this.listener = listener;
}
+ @Override
public AppSettings getSettings() {
return settings;
}
+ @Override
public com.jme3.renderer.Renderer getRenderer() {
return renderer;
}
+ @Override
public MouseInput getMouseInput() {
- return view;
+ return new DummyMouseInput();
}
+ @Override
public KeyInput getKeyInput() {
- return view;
+ return new DummyKeyInput();
}
-
+
+ @Override
public JoyInput getJoyInput() {
return null;
}
- public Timer getTimer() {
+ @Override
+ public TouchInput getTouchInput() {
+ return view;
+ }
+
+ @Override
+ public Timer getTimer()
+ {
return timer;
}
- public void setTitle(String title) {
+ @Override
+ public void setTitle(String title)
+ {
}
-
- public boolean isCreated(){
+
+ @Override
+ public boolean isCreated()
+ {
return created.get();
}
-
- public void setAutoFlushFrames(boolean enabled){
+ @Override
+ public void setAutoFlushFrames(boolean enabled)
+ {
this.autoFlush = enabled;
}
- // renderer:initialize
- public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
- logger.info("Using Android");
- initInThread(gl);
- }
+
// SystemListener:reshape
- public void onSurfaceChanged(GL10 gl, int width, int height) {
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height)
+ {
+ logger.info("GL Surface changed, width: " + width + " height: " + height);
settings.setResolution(width, height);
listener.reshape(width, height);
}
// SystemListener:update
- public void onDrawFrame(GL10 gl) {
- if (needClose.get()){
- deinitInThread(); // ???
+ @Override
+ public void onDrawFrame(GL10 gl)
+ {
+
+
+ if (needClose.get())
+ {
+ deinitInThread();
return;
}
+
+ if (renderable.get())
+ {
-// if (wasActive != Display.isActive()){
-// if (!wasActive){
-// listener.gainFocus();
-// wasActive = true;
-// }else{
-// listener.loseFocus();
-// wasActive = false;
-// }
-// }
-
- if (!created.get())
- throw new IllegalStateException();
+ if (!created.get())
+ throw new IllegalStateException("onDrawFrame without create");
- listener.update();
+ milliStart = System.currentTimeMillis();
+
- // swap buffers
-
- if (frameRate > 0){
-// Display.sync(frameRate);
- // synchronzie to framerate
+
+ listener.update();
+
+
+ if (autoFlush)
+ {
+ renderer.onFrame();
+ }
+
+ milliDelta = System.currentTimeMillis() - milliStart;
+
+ // Enforce a FPS cap
+ if (milliDelta < minFrameDuration)
+ {
+ //logger.log(Level.INFO, "Time per frame {0}", milliDelta);
+ try {
+ Thread.sleep(minFrameDuration - milliDelta);
+ } catch (InterruptedException e) {
+ }
+ }
+
}
-
- if (autoFlush)
- renderer.onFrame();
+// if (renderer.adreno_finish_bug) {
+// GLES20.glFinish();
+// }
}
-
- /**
- * TODO: get these methods to follow the spec
- * @param waitFor
- */
- public void create(boolean waitFor) {
- if (created.get()){
- logger.warning("create() called when display is already created!");
- return;
- }
+
+ @Override
+ public boolean isRenderable()
+ {
+ return renderable.get();
}
-
- public void create(){
- create(false);
+
+ @Override
+ public void create(boolean waitFor)
+ {
+ if (waitFor)
+ waitFor(true);
}
-
- public void restart() {
+
+ public void create()
+ {
+ create(false);
}
-
- public boolean isRenderable() {
- // TODO isRenderable
- return true;
+
+ @Override
+ public void restart()
+ {
+
}
- /**
- * TODO: get these methods to follow the spec
- * @param waitFor
- */
- public void destroy(boolean waitFor) {
+ @Override
+ public void destroy(boolean waitFor)
+ {
needClose.set(true);
+ if (waitFor)
+ waitFor(false);
+ }
+
+ public void destroy()
+ {
+ destroy(true);
+ }
+
+ protected void waitFor(boolean createdVal)
+ {
+ while (renderable.get() != createdVal){
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
}
- public void destroy(){
- destroy(false);
+ public int getClientOpenGLESVersion()
+ {
+ return clientOpenGLESVersion;
}
}