/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mt4j.components.css.util.CSSStyleManager;
import org.mt4j.input.IKeyListener;
import org.mt4j.input.InputManager;
import org.mt4j.input.inputData.AbstractCursorInputEvt;
import org.mt4j.input.inputData.ActiveCursorPool;
import org.mt4j.input.inputData.InputCursor;
import org.mt4j.input.inputProcessors.globalProcessors.AbstractGlobalInputProcessor;
import org.mt4j.input.inputSources.AbstractInputSource;
import org.mt4j.sceneManagement.IPreDrawAction;
import org.mt4j.sceneManagement.ISceneChangeListener;
import org.mt4j.sceneManagement.Iscene;
import org.mt4j.sceneManagement.SceneChangeEvent;
import org.mt4j.sceneManagement.transition.ITransition;
import org.mt4j.util.ArrayDeque;
import org.mt4j.util.PlatformUtil;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.animation.AnimationManager;
import org.mt4j.util.logging.ILogger;
import org.mt4j.util.opengl.GL10;
import org.mt4j.util.opengl.GL11;
import org.mt4j.util.opengl.GL11Plus;
import org.mt4j.util.opengl.GL20;
import org.mt4j.util.opengl.GLCommon;
import org.mt4j.util.opengl.GLTexture;
import org.mt4j.util.opengl.GLTexture.EXPANSION_FILTER;
import org.mt4j.util.opengl.GLTexture.SHRINKAGE_FILTER;
import org.mt4j.util.opengl.GLTexture.WRAP_MODE;
import org.mt4j.util.opengl.GLTextureSettings;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.core.PMatrix3D;
/**
* Use this class to create a new multitouch application.
* <br>The best way to create your application would be to extend this class and
* put the <code>main</code> method into that class.
* In the <code>main</code> method call the <code>initialize()</code> method.
* Then override the <code>startUp()</code> method which is called
* automatically after the initialize method. The <code>startUp()</code> method can be used to
* create your scenes (extend the <code>AbstractScene</code> class) and add them to
* the application by calling <code>addScene</code> method.
*
* <p>Internally, the main method of processings PApplet class is called with the class name
* of the extended PApplet class as an argument. The PApplet class then instantiates the given
* class and calls its setup() and then repeatedly its run() method.
*
* @author Christopher Ruff
*/
public abstract class AbstractMTApplication extends PApplet implements IMTApplication{
/** The Constant logger. */
protected static ILogger logger;
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The scene change locked. */
private boolean sceneChangeLocked;
// private static MTApplication mtApp = null;
/** The scene list. */
private List<Iscene> sceneList;
/** The current scene. */
private Iscene currentScene;
/** The animation mgr. */
protected AnimationManager animMgr;
/** The time last frame. */
private long timeLastFrame ;
/** The already run. */
private boolean alreadyRun;
/** The input manager. */
private InputManager inputManager;
/** The scene changed listeners. */
private List<ISceneChangeListener> sceneChangedListeners;
/** The invoke later actions. */
private ArrayDeque<Runnable> invokeLaterActions;
/** The scene stack. */
private ArrayDeque<Iscene> sceneStack;
protected Thread renderThread;
public static String separator = "/";
public static char separatorChar = '/';
// private static boolean settingsLoadedFromFile = false; //cant initialize in constructor, need it before that!
protected CSSStyleManager cssStyleManager;
protected ArrayDeque<IPreDrawAction> preDrawActions;
protected GLCommon glCommon;
protected GL10 iGL10;
protected GL11 iGL11;
protected GL20 iGL20;
protected GL11Plus iGL11Plus;
protected boolean gl20Supported;
protected boolean gl11Supported;
protected boolean gl11PlusSupported;
// private static boolean fullscreen;
/*
public static void main(String[] args){
// MTApplication app = new MTApplication();
PApplet.main(new String[] {
// "--present",
// "--exclusive",
"--bgcolor=#000000",
"--hide-stop",
"org.mt4j.MTApplication"
}
);
}
@Override
public void setup(){
size(800,600, OPENGL); //TODO REMOVE
logger.debug("Setup");
System.out.println("Setup called");
smooth();
hint(ENABLE_OPENGL_2X_SMOOTH );
smooth();
noSmooth();
background(0);
GL gl = Tools3D.getGL(this);
// gl.glEnable(GL.GL_MULTISAMPLE);
// gl.glEnable(GL.GL_MULTISAMPLE_EXT);
}
@Override
public void draw(){
// background(255);
fill(250,0,0,255);
stroke(250,0,0,255);
line(0,10, 280,20);
GL gl = Tools3D.beginGL(this);
// GL gl = ((PGraphicsOpenGL)this.g).beginGL();
// gl.glEnable(GL.GL_LINE_SMOOTH );
gl.glDisable(GL.GL_LINE_SMOOTH );
// gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
// Enable Blending
gl.glEnable(GL.GL_BLEND);
// Specifies pixel arithmetic
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl.glLineWidth(1);
gl.glColor4d(0.0, 0.0, 0.0, 1);
gl.glBegin(GL.GL_LINE_STRIP);
gl.glVertex3d(0, 20, 0);
gl.glVertex3d(280, 30, 0);
gl.glEnd();
gl.glBegin(GL.GL_LINE_STRIP);
gl.glVertex3d(0, 20, 0);
gl.glVertex3d(711, 230, 0);
gl.glVertex3d(200, 300, 0);
gl.glVertex3d(100, 330, 0);
gl.glEnd();
// ((PGraphicsOpenGL)this.g).endGL();
Tools3D.endGL(this);
if (this.mousePressed){
fill(150);
rect(mouseX, mouseY, 10,10);
}
}
*/
/*
//TODO test to make window undecorated - seems to mess up some textures (maybe because opengl re-initialization)
//put frame.setLocation(-1600, 0); at the end of setup() to position the frame
public void init(){
// to make a frame not displayable, you can
// use frame.removeNotify()
frame.removeNotify();
frame.setUndecorated(true);
// addNotify, here i am not sure if you have
// to add notify again.
frame.addNotify();
super.init();
}
*/
/**
* Dont instiatiate this class directly!
* It gets instantiated by the PApplet class via
* java reflection.
*/
public AbstractMTApplication(){
sceneList = new ArrayList<Iscene>();
currentScene = null;
animMgr = AnimationManager.getInstance();
alreadyRun = false;
sceneChangedListeners = new ArrayList<ISceneChangeListener>();
invokeLaterActions = new ArrayDeque<Runnable>();
sceneStack = new ArrayDeque<Iscene>();
sceneChangeLocked = false;
cssStyleManager = new CSSStyleManager(this);
preDrawActions = new ArrayDeque<IPreDrawAction>();
keyListeners = new ArrayList<IKeyListener>();
}
public void setOpenGLErrorReportingEnabled(boolean reportErros){
if (reportErros){
hint(AbstractMTApplication.ENABLE_OPENGL_ERROR_REPORT);
}else{
hint(AbstractMTApplication.DISABLE_OPENGL_ERROR_REPORT);
}
}
/**
* ********************************************************************************************
* Processings draw() gets called repeatedly by processings PApplet Class - unless noloop() is called
* ********************************************************************************************.
*/
@Override
public void draw(){
this.runApplication();
}
/**
* Is called at the end of the setup() method.
* <br>Override this method in your extended MTApplication class!
*/
public abstract void startUp();
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#registerPreDrawAction(org.mt4j.sceneManagement.IPreDrawAction)
*/
public void registerPreDrawAction(final IPreDrawAction action){
synchronized (preDrawActions) {
// this.preDrawActions.addLast(action);
invokeLater(new Runnable() {
public void run() {
preDrawActions.addLast(action);
}
});
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#unregisterPreDrawAction(org.mt4j.sceneManagement.IPreDrawAction)
*/
public void unregisterPreDrawAction(final IPreDrawAction action){
synchronized (preDrawActions) {
if (preDrawActions.contains(action)){
// this.preDrawActions.remove(action);
invokeLater(new Runnable() {
public void run() {
preDrawActions.remove(action);
}
});
}
}
}
/**
* Main run loop.
* <li>Updates the time passed since the last time drawn.
* <li>Updates any animations with the new time delta.
* <li>Updates and draws the current scene.
* <li>Updates and draws the current scene transitions.
*/
private void runApplication(){
//Process preDrawActions
synchronized (preDrawActions) {
for (Iterator<IPreDrawAction> iter = preDrawActions.iterator(); iter.hasNext();) {
IPreDrawAction action = iter.next();
action.processAction();
if (!action.isLoop()){
iter.remove();
}
}
}
//Use nanoTime
if (!alreadyRun){
alreadyRun = true;
timeLastFrame = System.nanoTime();
}
long nanos = System.nanoTime();
long timeDelta = (nanos - timeLastFrame) / 1000000L;
timeLastFrame = nanos;
/*
//Use currentTimeMillis
if (!alreadyRun){
alreadyRun = true;
timeLastFrame = System.currentTimeMillis();
}
long millis = System.currentTimeMillis();
long timeDelta = millis - timeLastFrame;
timeLastFrame = millis;
*/
// System.out.println("TimeDelta: " + timeDelta);
//Run invoke later actions
synchronized (invokeLaterActions) {
while (!invokeLaterActions.isEmpty()){
invokeLaterActions.pollFirst().run();
}
}
//Update animation manager
animMgr.update(timeDelta);
// /*
//Handle scene transitions
if (this.pendingTransition != null){
//Run the transition
this.pendingTransition.transition.drawAndUpdate(this.g, timeDelta);
if (this.pendingTransition.transition.isFinished()){
this.pendingTransition.transition.onLeave();
this.doSceneChange(this.getCurrentScene(), this.pendingTransition.nextScene);
this.pendingTransition = null;
}
}else{
//Draw the current scene
Iscene theCurrentScene = this.getCurrentScene();
if (theCurrentScene != null){
theCurrentScene.drawAndUpdate(this.g, timeDelta);
}
}
// */
/*
//Update scene
sceneMgr.updateCurrentScene(timeDelta);
//Draw scene
sceneMgr.drawCurrentScene();
*/
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#isRenderThreadCurrent()
*/
public boolean isRenderThreadCurrent(){
return Thread.currentThread().equals(renderThread);
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#invokeLater(java.lang.Runnable)
*/
public void invokeLater(Runnable runnable){
synchronized (invokeLaterActions) {
invokeLaterActions.addLast(runnable);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#peekScene()
*/
public Iscene peekScene(){
return sceneStack.peek();
}
protected int getSceneStackCount(){
return sceneStack.size();
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#pushScene()
*/
public void pushScene(){
if (getCurrentScene() == null){
logger.debug("Scene stack is empty! No scene to put on the stack!");
}else{
logger.debug("Putting scene: " + getCurrentScene().getName() + " on the stack.");
sceneStack.offerFirst(getCurrentScene());
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#popScene()
*/
public boolean popScene(){
// Iscene stackScene = sceneStack.pollFirst();
Iscene stackScene = sceneStack.peek();
if (stackScene != null){
logger.debug("Popping scene: " + stackScene.getName() + " back from the stack.");
boolean changed = this.changeScene(stackScene);
if (changed){
sceneStack.pollFirst();
return true;
}else{
return false;
}
}else{
logger.warn("Scene stack is empty! No scene to pop from the stack!");
return false;
}
}
private boolean inDoSceneChange = false;
private TransitionInfo pendingTransition;
/**
* The Class TransitionInfo. Holding info about a scene change transition.
* @author Christopher Ruff
*/
private class TransitionInfo{
ITransition transition;
Iscene lastScene;
Iscene nextScene;
boolean destroyLastSceneAfterTransition = false;
public TransitionInfo(ITransition transition, Iscene lastScene, Iscene nextScene){
this.transition = transition;
this.lastScene = lastScene;
this.nextScene = nextScene;
}
}
/**
* Initiates the scene change. Checks if the old scene has a transition
* and sets it to be used in the main loop.
*
* @param oldScene the old scene
* @param newScene the new scene
*/
private boolean initiateSceneChange(Iscene oldScene, Iscene newScene){
//FIXME TEST!
if (oldScene.equals(newScene)){
logger.error("Trying to change from and to the same scene.");
return false;
}
//Lock scene changes to only 1 at a time. At sending the bridge events during the
//scene change, it could occur that a scene change could be triggered again which we prevent
if (!sceneChangeLocked){
sceneChangeLocked = true;
Iscene lastScene = this.getCurrentScene();
//Remove pending animations //
//FIXME problemes, if new animations are defined in a scenes constructor, they get removed here..
//AnimationManager.getInstance().clear();
//Flush events so that enqueued input ended get sent to the last scene
//(Problem: they have been removed from active cursor pool already so they dont
//appear there and no ended and started evts are sent to the scenes!
//IF input started or updated should be flushed with this they should appear in active
//cursor list after that and be sended the right events
//- maybe only flush input_ended?
for (AbstractInputSource abstractInputSource : getInputManager().getInputSources()) {
abstractInputSource.flushEvents();
}
//Check which cursors are still active and clone their last evt as INPUT_ENDED
//so the scene can complete its state (i.e. buttons are be released etc)
this.sendEndedEvents(lastScene);
//Disable the last scene's global input processors
this.getInputManager().disableGlobalInputProcessors(lastScene);
// /*
if (lastScene.getTransition() != null){
ITransition t = lastScene.getTransition();
this.pendingTransition = new TransitionInfo(t, lastScene, newScene);
t.onEnter();
t.setup(lastScene, newScene);
return true;
}else{
return this.doSceneChange(lastScene, newScene);
}
// */
//doSceneChange(oldScene, newScene);
}else{
logger.debug("Couldnt change scene -> Change is locked from another scene change.");
return false;
}
}
/**
* Does the scene change after the transition (if existing) is completed.
* @param oldScene the old scene
* @param newScene the new scene
*/
private boolean doSceneChange(Iscene oldScene, Iscene newScene){
if (sceneChangeLocked && !inDoSceneChange){
inDoSceneChange = true;
//Maybe show loading progress for newScenne.Init first?
oldScene.onLeave();
//Initialize new Scene
newScene.onEnter();
//Enable input Processors previously registered with that scene
this.getInputManager().enableGlobalInputProcessors(newScene);
//Check which cursors are active and clone their last evt as INPUT_DETECTED
//so the scene doesent get INPUT_UPDATED without the start events
this.sendStartedEvents(newScene);
//Set new current scene
this.currentScene = newScene;
//FIXME TEST -> Make it possible to destroy scenes after a transition
//(During a transition the old scene cant be removed or destroyed because
//its still the current scene!)
if (pendingTransition != null){
if (pendingTransition.destroyLastSceneAfterTransition){
logger.debug("Destroying scene: " + pendingTransition.lastScene.getName() + " after the transition.");
pendingTransition.lastScene.destroy();
}
}
if (!this.sceneChangedListeners.isEmpty()){
this.fireSceneChangeEvent(new SceneChangeEvent(this, oldScene, newScene));
}
logger.debug("Scene changed from: '" + oldScene + "' to: '" + newScene + "'");
sceneChangeLocked = false;
inDoSceneChange = false;
return true;
}else{
return false;
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#changeScene(org.mt4j.sceneManagement.Iscene)
*/
public synchronized boolean changeScene(Iscene newScene){
if (!this.sceneList.contains(newScene)){
this.addScene(newScene);
}
return this.initiateSceneChange(this.getCurrentScene(), newScene);
}
/**
* Checks which cursors are active during the scene change and
* sends input_ended events of the active cursors to last scene's global input processors
* so actions in the last scene can be completed correctly.
* This means that one cursor can have more than one input_ended and input_started event
* in its event list!
*
* @param lastScene the last scene
* @param newScene the new scene
*/
private void sendEndedEvents(Iscene lastScene){
logger.debug("Sending INPUT_ENDED events to the last scene, Active motions: " + ActiveCursorPool.getInstance().getActiveCursorCount());
InputCursor[] activeCursors = ActiveCursorPool.getInstance().getActiveCursors();
for (InputCursor inputCursor : activeCursors) {
if (inputCursor.getCurrentEvent() != null) {
AbstractCursorInputEvt lastEvt = inputCursor.getCurrentEvent();
if (lastEvt.getId() != AbstractCursorInputEvt.INPUT_ENDED) {
try {
AbstractCursorInputEvt endedEvt = (AbstractCursorInputEvt) lastEvt.clone();
endedEvt.setId(AbstractCursorInputEvt.INPUT_ENDED);
endedEvt.onFired();
this.sendEvtToSceneProcessors(lastScene, endedEvt);
logger.debug("Sending INPUT_ENDED evt to scene: " + lastScene.getName() + " Cursor: " + endedEvt.getCursor());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* Checks which cursors are active during the scene change and
* sends input_started to the new scene's global input processors so actions in the
* last scene can be completed correctly.
* This means that one cursor can have more than one input_ended and input_started event
* in its event list!
*
* @param lastScene the last scene
* @param newScene the new scene
*/
private void sendStartedEvents(Iscene newScene){
logger.debug("Sending INPUT_DETECTED events to the new scene, Active motions: " + ActiveCursorPool.getInstance().getActiveCursorCount());
InputCursor[] activeCursors = ActiveCursorPool.getInstance().getActiveCursors();
for (InputCursor inputCursor : activeCursors) {
if (inputCursor.getCurrentEvent() != null) {
//PROBLEM: if in lastscene last event in cursor was input_started enqueued
//but not added to cursor yet,
//shall we send it again in new scene? -> will input_started be sent twice?
//- what if input started was enqueued during transition and not sent to any scene
AbstractCursorInputEvt lastEvt = inputCursor.getCurrentEvent();
/*
if (//lastEvt.getId() != AbstractCursorInputEvt.INPUT_DETECTED
true
){
*/
try {
AbstractCursorInputEvt startedEvt = (AbstractCursorInputEvt) lastEvt.clone();
startedEvt.setId(AbstractCursorInputEvt.INPUT_STARTED);
startedEvt.onFired();
this.sendEvtToSceneProcessors(newScene, startedEvt);
logger.debug("Sending INPUT_DETECTED evt to scene: " + newScene.getName() + " Cursor: " + startedEvt.getCursor());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// }
}
}
}
/**
* Send evt to scene processors.
*
* @param scene the scene
* @param evtToFire the evt to fire
*/
private void sendEvtToSceneProcessors(Iscene scene, AbstractCursorInputEvt evtToFire){
AbstractGlobalInputProcessor[] sceneInputProcessors = this.getInputManager().getGlobalInputProcessors(scene);
for (AbstractGlobalInputProcessor a : sceneInputProcessors) {
//Hack, because processInputEvt() is disabled at this moment! -> not anymore..
// a.processInputEvtImpl(evtToFire);
a.processInputEvent(evtToFire);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getCurrentScene()
*/
public Iscene getCurrentScene(){
return currentScene;
}
/*
public void drawCurrentScene(){
getCurrentScene().draw();
}
public void updateCurrentScene(long timeDelta){
getCurrentScene().update(timeDelta);
}
*/
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#addScene(org.mt4j.sceneManagement.Iscene)
*/
public void addScene(Iscene scene){
if (this.getSceneCount() == 0){
scene.onEnter();
this.currentScene = scene;
this.getInputManager().enableGlobalInputProcessors(scene);
this.fireSceneChangeEvent(new SceneChangeEvent(this, this.currentScene, this.currentScene));
}
if (!sceneList.contains(scene))
sceneList.add(scene);
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#addAll(org.mt4j.sceneManagement.Iscene[])
*/
public void addAll(Iscene[] scenes){
// if (this.getSceneCount() == 0 && scenes[0] != null){
// this.currentScene = scenes[0];
// }
for (Iscene scene : scenes) {
// sceneList.add(scene);
this.addScene(scene);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#removeScene(org.mt4j.sceneManagement.Iscene)
*/
public boolean removeScene(Iscene scene){
if (sceneList.contains(scene)){
if (scene.equals(this.currentScene)){
logger.warn("Cant remove the scene if it is the currently active scene! (" + scene + ")");
return false;
}else{
sceneList.remove(scene);
return true;
}
}
else{
return false;
}
// return true;
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#destroySceneAfterTransition(org.mt4j.sceneManagement.Iscene)
*/
public void destroySceneAfterTransition(Iscene scene){
if (pendingTransition != null && pendingTransition.lastScene.equals(scene)){
pendingTransition.destroyLastSceneAfterTransition = true;
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getScenes()
*/
public Iscene[] getScenes(){
return sceneList.toArray(new Iscene[sceneList.size()]);
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getScene(java.lang.String)
*/
public Iscene getScene(String name){
Iscene returnScene = null;
for(Iscene scene : sceneList){
if (scene.getName().equals(name))
returnScene = scene;
}
return returnScene;
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getSceneCount()
*/
public int getSceneCount(){
return sceneList.size();
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getInputManager()
*/
public InputManager getInputManager() {
return inputManager;
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#setInputManager(org.mt4j.input.InputManager)
*/
public void setInputManager(InputManager inputManager) {
this.inputManager = inputManager;
}
public PGraphics getPGraphics(){
return this.g;
}
/*
// public PMatrix3D getModelView() {
//// return ((PGraphics3D)this.g).modelview;
// return GraphicsUtil.getModelViewInv();
// }
////
// public PMatrix3D getModelViewInv() {
//// return ((PGraphics3D)this.g).modelviewInv;
// return GraphicsUtil.getModelView();
// }
// public GL10 beginGL() {
// ((PGraphicsOpenGL)this.g).beginGL();
// return this.iGL10;
// }
//
// public void endGL(){
// ((PGraphicsOpenGL)this.g).endGL();
// }
// */
// /*
public PMatrix3D getModelView() {
return PlatformUtil.getModelView();
}
public PMatrix3D getModelViewInv() {
return PlatformUtil.getModelViewInv();
}
public GL10 beginGL() {
PlatformUtil.beginGL();
return this.iGL10;
}
public void endGL(){
PlatformUtil.endGL();
}
// */
/**
* Returns whether OpenGL ES 1.1 is available. If it is you can get an instance of {@link GL11} via {@link #getGL11()} to
* access OpenGL ES 1.1 functionality. This also implies that {@link #getGL10()} will return an instance.
*
* @return whether OpenGL ES 1.1 is available
*/
public boolean isGL11Available (){
return this.gl11Supported;
}
public boolean isGL11PlusAvailable() {
return this.gl11PlusSupported;
}
/**
* Returns whether OpenGL ES 2.0 is available. If it is you can get an instance of {@link GL20} via {@link #getGL20()} to
* access OpenGL ES 2.0 functionality. Note that this functionality will only be available if you instructed the
* {@link Application} instance to use OpenGL ES 2.0!
*
* @return whether OpenGL ES 2.0 is available
*/
public boolean isGL20Available (){
return this.gl20Supported;
}
/**
* @return a {@link GLCommon} instance
*/
public GLCommon getGLCommon (){
return this.glCommon;
}
/**
* @return the {@link GL10} instance or null if not supported
*/
public GL10 getGL10 (){
return this.iGL10;
}
/**
* @return the {@link GL11} instance or null if not supported
*/
public GL11 getGL11 (){
return this.iGL11;
}
/**
* @return the {@link GL20} instance or null if not supported
*/
public GL20 getGL20 (){
return this.iGL20;
}
public GL11Plus getGL11Plus (){
return this.iGL11Plus;
}
/////////////////////////
/**
* Fire scene change event.
*
* @param sc the sc
*/
protected void fireSceneChangeEvent(SceneChangeEvent sc) {
for (ISceneChangeListener listener : sceneChangedListeners){
listener.processSceneChangeEvent(sc);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#addSceneChangeListener(org.mt4j.sceneManagement.ISceneChangeListener)
*/
public synchronized void addSceneChangeListener(ISceneChangeListener listener){
if (!this.sceneChangedListeners.contains(listener)){
sceneChangedListeners.add(listener);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#removeSceneChangeListener(org.mt4j.sceneManagement.ISceneChangeListener)
*/
public synchronized void removeSceneChangeListener(ISceneChangeListener listener){
if (sceneChangedListeners.contains(listener)){
sceneChangedListeners.remove(listener);
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getSceneChangeListener()
*/
public synchronized ISceneChangeListener[] getSceneChangeListener(){
return sceneChangedListeners.toArray(new ISceneChangeListener[this.sceneChangedListeners.size()]);
}
/////////////////////////////////
/**
* Gets the class name.
*
* @author C.Ruff
*/
public static class CurrentClassGetter extends SecurityManager {
/**
* Gets the class name.
*
* @return the class name
*/
public String getClassName() {
return getClassContext()[2].getName(); //FIXME is this reliable to always work?
}
}
/* (non-Javadoc)
* @see org.mt4j.IMTApplication#getCssStyleManager()
*/
public CSSStyleManager getCssStyleManager() {
return this.cssStyleManager;
}
//////////////////////////////////// Key Listener
/*
* Key checking example:
* Android: if (key == CODED && keyCode == KeyEvent.KEYCODE_BACK) check if key== CODED for special keys, esc, return etc
* Desktop: key == KeyEvent.VK_ESCAPE
*/
@Override
public void keyPressed() {
this.fireKeyPressed(this.key, this.keyCode);
}
@Override
public void keyReleased() {
this.fireKeyReleased(this.key, this.keyCode);
}
private ArrayList<IKeyListener> keyListeners;
protected void fireKeyPressed(char key, int keyCode) {
for (IKeyListener listener : keyListeners){
listener.keyPressed(key, keyCode);
}
}
protected void fireKeyReleased(char key, int keyCode) {
for (IKeyListener listener : keyListeners){
listener.keyRleased(key, keyCode);
}
}
public synchronized void addKeyListener(IKeyListener listener){
if (!this.keyListeners.contains(listener)){
keyListeners.add(listener);
}
}
public synchronized void removeKeyListener(IKeyListener listener){
if (keyListeners.contains(listener)){
keyListeners.remove(listener);
}
}
public synchronized IKeyListener[] getKeyListener(){
return keyListeners.toArray(new IKeyListener[this.keyListeners.size()]);
}
//////////////////////////////// KeyListener
//////////////////////////
//FIXME TEST
//-> to create gltexture automatically if using loadImage() in OpenGL mode
//-> prevents creating many opengl texture resources from the same PImage,
//e.g. if an PImage is loaded and then assigned to different shapes,
//in which a separate GLTexture object is created each time
@Override
public PImage loadImage(String filename) {
if (MT4jSettings.getInstance().isOpenGlMode()){
GLTextureSettings ts = new GLTextureSettings();
//Create new GLTexture from PImage
ts.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
ts.expansionFilter = EXPANSION_FILTER.Bilinear;
ts.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE;
ts.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE;
return new GLTexture(this, super.loadImage(filename), ts);
}else{
return super.loadImage(filename);
}
}
//////////////////////////////////////////////////
}