/***********************************************************************
* mt4j Copyright (c) 2008 - 2010 Christopher 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 advanced.mtShell;
import java.awt.event.KeyEvent;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.mt4j.MTApplication;
import org.mt4j.components.TransformSpace;
import org.mt4j.components.visibleComponents.font.FontManager;
import org.mt4j.components.visibleComponents.font.IFont;
import org.mt4j.components.visibleComponents.shapes.MTPolygon;
import org.mt4j.components.visibleComponents.widgets.MTList;
import org.mt4j.components.visibleComponents.widgets.MTListCell;
import org.mt4j.components.visibleComponents.widgets.MTSceneMenu;
import org.mt4j.components.visibleComponents.widgets.MTSceneWindow;
import org.mt4j.components.visibleComponents.widgets.MTTextArea;
import org.mt4j.input.gestureAction.InertiaDragAction;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.MTGestureEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapEvent;
import org.mt4j.input.inputProcessors.componentProcessors.tapProcessor.TapProcessor;
import org.mt4j.input.inputProcessors.globalProcessors.CursorTracer;
import org.mt4j.sceneManagement.AbstractScene;
import org.mt4j.sceneManagement.Iscene;
import org.mt4j.sceneManagement.transition.BlendTransition;
import org.mt4j.sceneManagement.transition.FadeTransition;
import org.mt4j.util.MTColor;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLFBO;
import processing.core.PApplet;
import processing.core.PImage;
import scenes.WaterSceneExportObf;
import sun.misc.GC;
import advanced.drawing.MainDrawingScene;
import advanced.flickrMT.FlickrScene;
import advanced.fluidSimulator.FluidSimulationScene;
import advanced.models3D.Models3DScene;
import advanced.modestMapsMT.MapsScene;
import advanced.physics.scenes.AirHockeyScene;
import advanced.physics.scenes.PhysicsScene;
import advanced.puzzle.PuzzleScene;
import advanced.space3D.Space3DScene;
import advanced.touchTail.TouchTailScene;
/**
* The Class MTShellScene. A scene which displays other scenes icons and loads them.
*
* @author Christopher Ruff
*/
public class MTShellScene extends AbstractScene {
/** The Constant logger. */
private static final Logger logger = Logger.getLogger(MTShellScene.class.getName());
static{
// logger.setLevel(Level.WARN);
// logger.setLevel(Level.DEBUG);
logger.setLevel(Level.INFO);
SimpleLayout l = new SimpleLayout();
ConsoleAppender ca = new ConsoleAppender(l);
logger.addAppender(ca);
}
/** The app. */
private MTApplication app;
/** The has fbo. */
private boolean hasFBO;
/** The list. */
private MTList list;
/** The font. */
private IFont font;
/** The preferred icon height. */
private int preferredIconHeight;
/** The gap between icon and reflection. */
private int gapBetweenIconAndReflection;
/** The display height of reflection. */
private float displayHeightOfReflection;
/** The list width. */
private float listWidth;
/** The list height. */
private int listHeight;
/** The preferred icon width. */
private int preferredIconWidth;
/** The switch directly to scene. */
private boolean switchDirectlyToScene = false;
private boolean useBackgroundGradient = true;
//TODO (dont allow throwing stuff out of the screen) or destroy it then
//TODO loading screen
/**
* Instantiates a new mT shell scene.
*
* @param mtApplication the mt application
* @param name the name
*/
public MTShellScene(MTApplication mtApplication, String name) {
super(mtApplication, name);
this.app = mtApplication;
this.hasFBO = GLFBO.isSupported(app);
// this.hasFBO = false; // TEST
//IF we have no FBO directly switch to scene and ignore setting
this.switchDirectlyToScene = !this.hasFBO? true : switchDirectlyToScene;
this.registerGlobalInputProcessor(new CursorTracer(app, this));
// this.setClearColor(new MTColor(230,230,230,255));
// this.setClearColor(new MTColor(30,30,30,255));
this.setClearColor(new MTColor(0,0,0,255));
//BACKGROUND GRADIENT
if (this.useBackgroundGradient){
Vertex[] vertices = new Vertex[]{
new Vertex(0, app.height/3f, 0, 0,0,0,255),
new Vertex(app.width, app.height/3, 0, 0,0,0,255),
new Vertex(app.width, app.height/1.7f,0, 170,170,140,255),
new Vertex(0, app.height/1.7f,0, 170,170,140,255),
new Vertex(0, app.height/3, 0, 0,0,0,255),
};
MTPolygon p = new MTPolygon(vertices, getMTApplication());
p.setName("upper gradient");
p.setNoStroke(true);
p.generateAndUseDisplayLists();
p.setPickable(false);
this.getCanvas().addChild(p);
Vertex[] vertices2 = new Vertex[]{
new Vertex(0, app.height/1.7f, 0, 170,170,140,255),
new Vertex(app.width, app.height/1.7f, 0, 170,170,140,255),
new Vertex(app.width, app.height, 0, 0,0,0,255),
new Vertex(0, app.height, 0, 0,0,0,255),
new Vertex(0, app.height/1.7f, 0, 170,170,140,255),
};
MTPolygon p2 = new MTPolygon(vertices2, getMTApplication());
p2.setNoStroke(true);
p2.generateAndUseDisplayLists();
p2.setPickable(false);
this.getCanvas().addChild(p2);
}
//BACKGROUND
preferredIconWidth = 256;
preferredIconHeight = 192;
gapBetweenIconAndReflection = 9;
displayHeightOfReflection = preferredIconHeight * 0.6f;
//CREATE LIST
listWidth = preferredIconHeight + displayHeightOfReflection + gapBetweenIconAndReflection;
// listHeight = app.width - 50;
listHeight = app.width;
list = new MTList(0,0, listWidth, listHeight, 40, mtApplication);
list.setFillColor(new MTColor(150,150,150,200));
list.setNoFill(true);
list.setNoStroke(true);
/*
//List ends fade images //Background gradient has to be set to false!
PImage gradImage = app.loadImage(this.getPathToIcons() + "gradx3.png");
MTRectangle grad1 = new MTRectangle(gradImage, app);
grad1.setUseDirectGL(true);
grad1.setNoStroke(true);
grad1.setPickable(false);
list.addChild(grad1);
grad1.setFillColor(this.getClearColor());
MTRectangle grad2 = new MTRectangle(0, listHeight-gradImage.height , gradImage.width, gradImage.height, app);
grad2.rotateZ(grad2.getCenterPointLocal(), 180, TransformSpace.LOCAL);
grad2.setTexture(gradImage);
grad2.setNoStroke(true);
grad2.setPickable(false);
list.addChild(grad2);
grad2.setFillColor(this.getClearColor());
*/
//TODO does the font exist on all platforms? fallback to arial.ttf?
font = FontManager.getInstance().createFont(app, "SansSerif", 18, MTColor.WHITE, MTColor.WHITE);
// font = FontManager.getInstance().createFont(app, "arial.ttf", 18, new MTColor(255,255,255), new MTColor(255,255,255));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new TouchTailScene(app, "Touch Tails");
}
public String getTitle() {
return "Touch Tails";
}
}, app.loadImage(this.getPathToIcons() + "touchtails_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new MapsScene(app, "Maps");
}
public String getTitle() {
return "Maps";
}
}, app.loadImage(this.getPathToIcons() + "maps_ss.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new AirHockeyScene(app, "Air Hockey");
}
public String getTitle() {
return "Air Hockey";
}
}, app.loadImage(this.getPathToIcons() + "airhockey_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new PuzzleScene(app, "Puzzle");
}
public String getTitle() {
return "Puzzle";
}
}, app.loadImage(this.getPathToIcons() + "puzz.png"));
if (this.hasFBO){
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new MainDrawingScene(app, "MT Paint");
}
public String getTitle() {
return "MT Paint";
}
}, app.loadImage(this.getPathToIcons() + "drawing_s.png"));
}
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new FlickrScene(app, "Flickr");
}
public String getTitle() {
return "Photo Search";
}
}, app.loadImage(this.getPathToIcons() + "flickr_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new WaterSceneExportObf(app, "Interactive Water");
}
public String getTitle() {
return "Interactive Water";
}
}, app.loadImage(this.getPathToIcons() + "water_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new FluidSimulationScene(app, "Fluid Particles");
}
public String getTitle() {
return "Fluid Particles";
}
}, app.loadImage(this.getPathToIcons() + "fluidparticles_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new Models3DScene(app, "3D Models");
}
public String getTitle() {
return "3D Models";
}
}, app.loadImage(this.getPathToIcons() + "teapot.jpg"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new PhysicsScene(app, "Physics Playground");
}
public String getTitle() {
return "Physics Playground";
}
}, app.loadImage(this.getPathToIcons() + "pyhsics_s.png"));
this.addScene(new ICreateScene() {
public Iscene getNewScene() {
return new Space3DScene(app, "Earth 3D");
}
public String getTitle() {
return "Earth 3D";
}
}, app.loadImage(this.getPathToIcons() + "earth_s.png"));
getCanvas().addChild(list);
list.rotateZ(list.getCenterPointLocal(), -90, TransformSpace.LOCAL);
// list.setPositionGlobal(new Vector3D(app.width/2f, app.height - list.getHeightXY(TransformSpace.GLOBAL) - 1));
list.setPositionGlobal(new Vector3D(app.width/2f, app.height/2f));
// list.setAnchor(PositionAnchor.UPPER_LEFT);
// list.setPositionGlobal(new Vector3D(app.width/2f - list.getWidthXY(TransformSpace.GLOBAL)/2f, app.height - 20));
getCanvas().setFrustumCulling(true);
//Scene transition effect
if (this.hasFBO){
this.setTransition(new BlendTransition(app, 730));
}else{
this.setTransition(new FadeTransition(app, 730));
}
}
/**
* Gets the path to icons.
*
* @return the path to icons
*/
private String getPathToIcons(){
// return System.getProperty("user.dir")+File.separator+"examples"+File.separator+"advanced"+File.separator+"mtShell"+ File.separator +"data"+ File.separator+"images"+File.separator;
//Load from classpath
return "advanced" + MTApplication.separator + "mtShell" + MTApplication.separator + "data"+ MTApplication.separator + "images" + MTApplication.separator;
}
/**
* Adds the tap processor.
*
* @param cell the cell
* @param createScene the create scene
*/
private void addTapProcessor(MTListCell cell, final ICreateScene createScene){
cell.registerInputProcessor(new TapProcessor(app, 15));
cell.addGestureListener(TapProcessor.class, new IGestureEventListener() {
public boolean processGestureEvent(MTGestureEvent ge) {
TapEvent te = (TapEvent)ge;
if (te.isTapped()){
//System.out.println("Clicked cell: " + te.getTargetComponent());
final Iscene scene = createScene.getNewScene();
if (!switchDirectlyToScene){//We have FBO support -> show scene in a window first
if (hasFBO && scene instanceof AbstractScene){
((AbstractScene) scene).setTransition(new BlendTransition(app, 300));
}
final MTSceneWindow sceneWindow = new MTSceneWindow(scene, 100,50, app);
sceneWindow.setFillColor(new MTColor(50,50,50,200));
sceneWindow.scaleGlobal(0.5f, 0.5f, 0.5f, sceneWindow.getCenterPointGlobal());
sceneWindow.addGestureListener(DragProcessor.class, new InertiaDragAction());
getCanvas().addChild(sceneWindow);
}else{
//No FBO available -> change to the new scene fullscreen directly
float menuWidth = 64;
float menuHeight = 64;
MTSceneMenu sceneMenu =
//new MTSceneMenu(this, app.width-menuWidth/2f, 0-menuHeight/2f, menuWidth, menuHeight, app);
new MTSceneMenu(scene, app.width-menuWidth, app.height-menuHeight, menuWidth, menuHeight, app);
sceneMenu.addToScene();
app.addScene(scene);
app.pushScene();
app.changeScene(scene);
}
}
return false;
}
});
}
/**
* Adds the scene.
*
* @param sceneToCreate the scene to create
* @param icon the icon
*/
public void addScene(ICreateScene sceneToCreate, PImage icon){
// System.out.println("Width: " + width + " Height:" + height);
//Create reflection image
PImage reflection = this.getReflection(getMTApplication(), icon);
float border = 1;
float bothBorders = 2*border;
float topShift = 30;
float reflectionDistanceFromImage = topShift + gapBetweenIconAndReflection; //Gap width between image and reflection
float listCellWidth = listWidth;
float realListCellWidth = listCellWidth - bothBorders;
// float listCellHeight = preferredIconWidth - border;
float listCellHeight = preferredIconWidth ;
MTListCell cell = new MTListCell(realListCellWidth , listCellHeight, app);
cell.setNoFill(true);
cell.setNoStroke(true);
// /*
Vertex[] vertices = new Vertex[]{
new Vertex(realListCellWidth-topShift, border, 0, 0,0),
new Vertex(realListCellWidth-topShift, listCellHeight -border, 0, 1,0),
new Vertex(realListCellWidth-topShift - icon.height, listCellHeight -border, 0, 1,1),
new Vertex(realListCellWidth-topShift - icon.height, border, 0, 0,1),
new Vertex(realListCellWidth-topShift, border, 0, 0,0),
};
MTPolygon p = new MTPolygon(vertices, getMTApplication());
p.setTexture(icon);
// p.setNoStroke(true);
// p.setStrokeColor(new MTColor(150,150,150, 255));
p.setStrokeColor(new MTColor(80,80,80, 255));
Vertex[] verticesRef = new Vertex[]{
new Vertex(listCellWidth - icon.height - reflectionDistanceFromImage, border, 0, 0,0),
new Vertex(listCellWidth - icon.height - reflectionDistanceFromImage, listCellHeight -border, 0, 1,0),
new Vertex(listCellWidth - icon.height - reflection.height - reflectionDistanceFromImage, listCellHeight -border, 0, 1,1),
new Vertex(listCellWidth - icon.height - reflection.height - reflectionDistanceFromImage, border, 0, 0,1),
new Vertex(listCellWidth - icon.height - reflectionDistanceFromImage, border, 0, 0,0),
};
MTPolygon pRef = new MTPolygon(verticesRef, getMTApplication());
pRef.setTexture(reflection);
pRef.setNoStroke(true);
cell.addChild(p);
cell.addChild(pRef);
list.addListElement(cell);
addTapProcessor(cell, sceneToCreate);
// */
/*
MTRectangle rect = new MTRectangle(icon, getMTApplication());
rect.setUseDirectGL(true);
// rect.setNoStroke(true);
rect.rotateZ(rect.getCenterPointLocal(), 90, TransformSpace.LOCAL);
MTRectangle reflectionRect = new MTRectangle(0, height, width, height, getMTApplication());
reflectionRect.setTexture(reflection);
reflectionRect.setUseDirectGL(true);
// reflectionRect.setNoStroke(true);
reflectionRect.rotateZ(rect.getCenterPointLocal(), 90, TransformSpace.LOCAL);
cell.addChild(rect);
cell.addChild(reflectionRect);
list.addListElement(cell);
addTapProcessor(cell, sceneToCreate);
rect.setAnchor(PositionAnchor.UPPER_LEFT);
reflectionRect.setAnchor(PositionAnchor.UPPER_LEFT);
PositionAnchor oldCellAnchor = cell.getAnchor();
cell.setAnchor(PositionAnchor.UPPER_LEFT);
rect.setPositionRelativeToParent(cell.getPosition(TransformSpace.LOCAL));
rect.translate(new Vector3D(realListCellWidth - topShift, 0));
reflectionRect.setPositionRelativeToParent(cell.getPosition(TransformSpace.LOCAL));
reflectionRect.translate(new Vector3D(realListCellWidth - topShift - icon.height - gapBetweenIconAndReflection, 0));
cell.setAnchor(oldCellAnchor);
// rect.setPositionRelativeToParent(cell.getCenterPointLocal());
// MTRoundRectangle r = new MTRoundRectangle(0, 0, 0, listCellWidth, listCellHeight, 30, 30, getMTApplication());
// cell.addChild(r);
// getCanvas().addChild(rect);
*/
///Add scene title
MTTextArea text = new MTTextArea(app, font);
text.setFillColor(new MTColor(150,150,250,200));
text.setNoFill(true);
text.setNoStroke(true);
text.setText(sceneToCreate.getTitle());
text.rotateZ(text.getCenterPointLocal(), 90, TransformSpace.LOCAL);
cell.addChild(text);
// text.setAnchor(PositionAnchor.CENTER);
// PositionAnchor oldCellAnchor = cell.getAnchor();
// cell.setAnchor(PositionAnchor.CENTER);
// text.setPositionRelativeToParent(cell.getPosition(TransformSpace.LOCAL));
// text.translate(new Vector3D(realListCellWidth - topShift, 0));
// cell.setAnchor(oldCellAnchor);
text.setPositionRelativeToParent(cell.getCenterPointLocal());
text.translate(new Vector3D(realListCellWidth*0.5f - text.getHeightXY(TransformSpace.LOCAL)*0.5f, 0));
///
}
/**
* Creates the reflection image.
*
* @param pa the pa
* @param image the image
* @return the reflection image
*/
private PImage getReflection(PApplet pa, PImage image) {
int width = image.width;
int height = image.height;
PImage copyOfImage = pa.createImage(image.width, image.height, PApplet.ARGB);
image.loadPixels();
copyOfImage.loadPixels();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int imageIndex = y*image.width+x;
// int currA = (image.pixels[imageIndex] >> 32) & 0xFF;
int currR = (image.pixels[imageIndex] >> 16) & 0xFF;
int currG = (image.pixels[imageIndex] >> 8) & 0xFF;
int currB = image.pixels[imageIndex] & 0xFF;
int col = image.pixels[imageIndex];
float alpha = pa.alpha(col);
int reflectImageIndex = (image.height-y-1) * image.width+x;
//TOD clamp 0-255, map 0-255, 255- y*y * x
// copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , Math.round(y*0.8));
// copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , Math.round(y * (0.005f*y) * 0.5));
// copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , Math.round(Tools3D.clamp(255 - y*y , 0, 255)));
// copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , Math.round(y*y*y * (0.00003f) - 20)); //WORKS
if (alpha <= 0.0f){
copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , 0.0f);
}else{
copyOfImage.pixels[reflectImageIndex] = pa.color(currR , currG , currB , Math.round(y*y*y * (0.00003f) - 60)); //WORKS
}
}
}
copyOfImage.updatePixels();
return copyOfImage;
}
/* (non-Javadoc)
* @see org.mt4j.sceneManagement.AbstractScene#init()
*/
@Override
public void init() {
getMTApplication().registerKeyEvent(this);
}
/* (non-Javadoc)
* @see org.mt4j.sceneManagement.AbstractScene#shutDown()
*/
@Override
public void shutDown() {
getMTApplication().unregisterKeyEvent(this);
}
/**
* Key event.
*
* @param e the e
*/
public void keyEvent(KeyEvent e){
int evtID = e.getID();
if (evtID != KeyEvent.KEY_PRESSED)
return;
switch (e.getKeyCode()){
case KeyEvent.VK_F:
System.out.println("FPS: " + getMTApplication().frameRate);
break;
case KeyEvent.VK_M:
System.out.println("Max memory: " + Runtime.getRuntime().maxMemory() + " <-> Free memory: " + Runtime.getRuntime().freeMemory());
break;
case KeyEvent.VK_C:
getMTApplication().invokeLater(new Runnable() {
public void run() {
System.gc();
GC.maxObjectInspectionAge();
System.runFinalization();
}
});
break;
default:
break;
}
}
}