/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox 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 any later version. Jukefox 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
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.pancho3.view.tabs.opengl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLU;
import ch.ethz.dcg.jukefox.commons.DataUnavailableException;
import ch.ethz.dcg.jukefox.commons.utils.JoinableThread;
import ch.ethz.dcg.jukefox.commons.utils.Log;
import ch.ethz.dcg.jukefox.commons.utils.kdtree.KdTreePoint;
import ch.ethz.dcg.jukefox.model.AndroidCollectionModelManager;
import ch.ethz.dcg.jukefox.model.collection.BaseAlbum;
import ch.ethz.dcg.jukefox.model.collection.MapAlbum;
import ch.ethz.dcg.jukefox.model.collection.MapTag;
import ch.ethz.dcg.jukefox.model.libraryimport.ImportState;
import ch.ethz.dcg.pancho3.commons.settings.ISettingsReader;
import ch.ethz.dcg.pancho3.view.tabs.SpaceActivity;
public class SpaceRenderer extends BaseAlbumRenderer {
protected static final String TAG = SpaceRenderer.class.getSimpleName();
private RegionPlaylistCreator regionPlaylist = null;
private boolean highlightCurrentAlbum;
private SpaceActivity spaceActivity;
private GlHighlight glHighlight;
// private static final int PLANE_TAG_THRESHHOLD = 50;
// private static final int NUM_MAP_TAGS = 100;
public static final float CAMERA_HEIGHT = 1.7f;
private LinkedList<KdTreePoint<GlAlbumCover>> visibleAlbums;
private boolean doSelection;
private float[] clickPos;
private MapAlbum selectedAlbum;
// private long lastLogTime;
private AndroidCollectionModelManager data;
private ISettingsReader settings;
public SpaceRenderer(ISettingsReader settings, AndroidCollectionModelManager data,
ImportState importState, Context context, SpaceActivity spaceActivity) {
super(data, importState, context);
this.spaceActivity = spaceActivity;
this.data = data;
this.settings = settings;
Collection<MapAlbum> mapAlbums;
try {
mapAlbums = data.getAlbumProvider().getAllMapAlbums();
} catch (DataUnavailableException e) {
Log.w(TAG, e);
mapAlbums = new ArrayList<MapAlbum>();
}
Log.v(TAG, "Got " + mapAlbums.size() + " mapAlbums");
createGlAlbums(mapAlbums, false);
Log.v(TAG, "Created GL albums");
setPosition(settings);
// List<MapTag> mapTags =
// data.getMostRelevantTagsBlocking(NUM_MAP_TAGS);
List<MapTag> mapTags = data.getTagProvider().getAllMapTags();
createGlTags(mapTags, false);
Log.v(TAG, "Created " + mapTags.size() + " GlTags");
highlightCurrentAlbum = settings.isCurrentAlbumHighlighted();
glHighlight = new GlHighlight(0, 0, 0, false, spaceActivity);
// camera.setCameraPosition(0, CAMERA_HEIGHT, 0, false);
camera.setYBorders(0, camera.getRearClippingPlane());
}
private void setPosition(ISettingsReader settings) {
if (settings.isGotoCurrentAlbumEnabled()) {
MapAlbum album = spaceActivity.getCurrentAlbum();
if (album != null) {
goToAlbum(album);
Log.v(TAG, "Set camera to album: " + album.getName());
return;
}
}
float posX = settings.getLastPositionInPcaMapX();
float posZ = settings.getLastPositionInPcaMapY() + 1.5f * camera.getFrontClippingPlane();
// First set map to last position
camera.setCameraPosition(posX, SpaceRenderer.CAMERA_HEIGHT, posZ, false);
Log.v(TAG, "Set camera to position x: " + posX + " y: " + posZ);
}
@Override
public void drawFrame(GL10 gl) {
super.drawFrame(gl);
// log("Begin draw");
float camPosZ = getCamera().getPosZ();
float camPosX = getCamera().getPosX();
float camPosY = getCamera().getPosY();
// log("Before update cam");
updateModelViewWithCamera(gl, camPosZ, camPosX, camPosY);
// log("After update cam");
float minDistToCenter = Float.MAX_VALUE;
// log("Before selection");
if (doSelection) {
doSelection(gl, camPosZ, camPosX, minDistToCenter);
}
// log("After selection");
doRegionPlaylist(gl);
drawAlbums(gl, camPosZ, camPosX, minDistToCenter);
}
// private void log(String msg) {
// long currentTime = System.currentTimeMillis();
// Log.v(TAG, msg + " t: " + (currentTime - lastLogTime));
// lastLogTime = currentTime;
// }
private void doSelection(GL10 gl, float camPosZ, float camPosX, float minDistToCenter) {
Log.v(TAG, "doSelection()");
selectedAlbum = null;
clickPos[1] = getViewHeight() - clickPos[1];
ByteBuffer result = ByteBuffer.allocate(4);
float distantBorder = camPosZ - camera.getRearClippingPlane();
float closeBorder = camPosZ - camera.getFrontClippingPlane();
drawAlbumsInSelectionMode(gl, distantBorder, closeBorder);
gl.glReadPixels((int) clickPos[0], (int) clickPos[1], 1, 1, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, result);
int clickRed = result.get(0) & 0xFF;
int clickGreen = result.get(1) & 0xFF;
int clickBlue = result.get(2) & 0xFF;
if (clickRed == 0 && clickGreen == 0 && clickBlue == 0) {
doSelection = false;
Log.v(TAG, "Clicked in empty space");
return;
}
getSelection(clickRed, clickGreen, clickBlue, distantBorder, closeBorder);
doSelection = false;
}
private void getSelection(int clickRed, int clickGreen, int clickBlue, float distantBorder, float closeBorder) {
int red = 25;
int green = 25;
int blue = 25;
float minDist = Float.MAX_VALUE;
for (GlAlbumCover glAlbumCover : albums) {
MapAlbum mapAlbum = glAlbumCover.getMapAlbum();
if (mapAlbum.getGridCoords()[1] < distantBorder) {
continue;
}
if (mapAlbum.getGridCoords()[1] > closeBorder) {
break;
}
float distance = getColorDistance(red, clickRed, green, clickGreen, blue, clickBlue);
if (distance < minDist) {
minDist = distance;
selectedAlbum = mapAlbum;
}
red += 25;
if (red > 250) {
red = 25;
green += 25;
if (green > 250) {
green = 25;
blue += 25;
if (blue > 250) {
blue = 25;
red = 25;
}
}
}
}
}
private void drawAlbumsInSelectionMode(GL10 gl, float distantBorder, float closeBorder) {
int red = 25;
int green = 25;
int blue = 25;
for (GlAlbumCover glAlbumCover : albums) {
MapAlbum mapAlbum = glAlbumCover.getMapAlbum();
if (mapAlbum.getGridCoords()[1] < distantBorder) {
continue;
}
if (mapAlbum.getGridCoords()[1] > closeBorder) {
break;
}
glAlbumCover.drawForSelection(gl, red, green, blue);
red += 25;
if (red > 250) {
red = 25;
green += 25;
if (green > 250) {
green = 25;
blue += 25;
if (blue > 250) {
blue = 25;
red = 25;
}
}
}
}
}
private float getColorDistance(float red, int clickRed, float green, int clickGreen, float blue, int clickBlue) {
return (red - clickRed) * (red - clickRed) + (green - clickGreen) * (green - clickGreen) + (blue - clickBlue)
* (blue - clickBlue);
}
private void updateModelViewWithCamera(GL10 gl, float camPosZ, float camPosX, float camPosY) {
gl.glDisable(GL10.GL_DITHER);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, camPosX, camPosY, camPosZ, camPosX, camPosY - 0.2f, camPosZ - 1, 0f, 1.0f, 0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
private void drawAlbums(GL10 gl, float camPosZ, float camPosX, float minDistToCenter) {
float distantBorder = camPosZ - camera.getRearClippingPlane();
float closeBorder = camPosZ - camera.getFrontClippingPlane();
GlAlbumCover albumForCoverLoad = null;
// if (tags.size() == 0) {
// return;
// }
Iterator<GlTagLabel> tagIterator = tags.iterator();
GlTagLabel currentTag = null;
if (tags.size() > 0) {
currentTag = tagIterator.next();
}
for (GlAlbumCover glAlbumCover : albums) {
MapAlbum mapAlbum = glAlbumCover.getMapAlbum();
if (mapAlbum.getGridCoords()[1] < distantBorder) {
// Log.v(TAG, "not drawing album at: z: " +
// mapAlbum.getGridCoords()[1]);
continue;
}
if (mapAlbum.getGridCoords()[1] > closeBorder) {
// Log.v(TAG, "not drawing album at: z: " +
// mapAlbum.getGridCoords()[1]);
break;
}
while (tags.size() > 0 && tagIterator.hasNext() && currentTag.getPosZ() < mapAlbum.getGridCoords()[1]) {
drawTag(currentTag, camPosZ, gl, distantBorder, closeBorder);
currentTag = tagIterator.next();
}
if (!glAlbumCover.isTextureLoaded()) {
float diffX = camPosX - mapAlbum.getGridCoords()[0];
float diffZ = camPosZ - mapAlbum.getGridCoords()[1];
if (diffX < 0) {
diffX = -diffX;
}
if (diffZ < 0) {
diffZ = -diffZ;
}
float totalDiff = diffX + diffZ;
if (totalDiff < minDistToCenter) {
minDistToCenter = totalDiff;
albumForCoverLoad = glAlbumCover;
}
}
if (highlightCurrentAlbum && spaceActivity.getCurrentAlbum() != null
&& spaceActivity.getCurrentAlbum().getId() == mapAlbum.getId()) {
drawHighlight(gl);
}
// Log.v(TAG, "drawing albums");
glAlbumCover.draw(gl, true);
}
setAlbumToLoadAlbumArt(albumForCoverLoad);
}
private void drawTag(GlTagLabel currentTag, float camPosZ, GL10 gl, float distantBorder, float closeBorder) {
if (currentTag.getPosZ() < distantBorder || currentTag.getPosZ() > closeBorder) {
return;
}
currentTag.draw(gl);
}
private void drawHighlight(GL10 gl) {
// Maybe draw a highlight
if (highlightCurrentAlbum && spaceActivity.getCurrentAlbum() != null) {
MapAlbum album = spaceActivity.getCurrentAlbum();
glHighlight.setCoords(album.getGridCoords()[0], 0f, album.getGridCoords()[1] - 0.01f);
glHighlight.draw(gl, albumTextures[0]);
// Log.v(TAG, "drawn highlight");
}
}
private void doRegionPlaylist(GL10 gl) {
if (regionPlaylist != null) {
regionPlaylist.draw(gl);
if (regionPlaylist.hasToGetAlbums()) {
regionPlaylist.getAlbumsInPlaylist(gl);
regionPlaylist = null;
}
}
}
public void startDrawingRegionPlaylist(RegionPlaylistCreator regionPlaylist) {
this.regionPlaylist = regionPlaylist;
}
public void stopDrawingRegionPlaylist() {
if (regionPlaylist != null) {
regionPlaylist.createPlaylist();
}
}
public LinkedList<KdTreePoint<GlAlbumCover>> getVisibleAlbums() {
return visibleAlbums;
}
public void onResume() {
for (GlAlbumCover albumCover : albums) {
albumCover.resetTexture();
}
for (GlTagLabel tag : tags) {
tag.resetTexture();
}
glHighlight = new GlHighlight(0, 0, 0, false, spaceActivity);
}
public void onPause() {
if (albumArtTextureLoader != null) {
albumArtTextureLoader.terminate();
}
}
public BaseAlbum getSelection(float touchPosX, float touchPosY) {
doSelection = true;
clickPos = new float[] { touchPosX, touchPosY };
while (doSelection == true) {
try {
JoinableThread.sleep(50);
} catch (InterruptedException e) {
Log.w(TAG, e);
}
}
return selectedAlbum;
}
public void newCurrentAlbum(MapAlbum currentAlbum) {
if (settings.isGotoCurrentAlbumEnabled()) {
MapAlbum album = spaceActivity.getCurrentAlbum();
if (album != null) {
goToAlbum(album);
Log.v(TAG, "Set camera to album: " + album.getName());
return;
}
}
}
public void goToAlbum(BaseAlbum album) {
if (album == null) {
return;
}
MapAlbum mapAlbum;
try {
mapAlbum = data.getAlbumProvider().getMapAlbum(album);
camera.setCameraPosition(mapAlbum.getGridCoords()[0], CAMERA_HEIGHT, mapAlbum.getGridCoords()[1] + 1.5f
* camera.getFrontClippingPlane(), false);
Log.v(TAG, "Set camera to album: " + album.getName());
} catch (DataUnavailableException e) {
Log.w(TAG, e);
}
}
}