package gov.nasa.jpl.hi.marsimages.models;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.View;
import com.evernote.edam.type.Note;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import gov.nasa.jpl.hi.marsimages.rovers.Rover;
import gov.nasa.jpl.hi.marsimages.ui.MarsMosaicRenderer;
import rajawali.Camera;
import rajawali.materials.SimpleMaterial;
import rajawali.materials.TextureInfo;
import rajawali.materials.TextureManager;
import rajawali.math.Number3D;
import static android.opengl.GLES20.GL_TRIANGLE_FAN;
import static gov.nasa.jpl.hi.marsimages.EvernoteMars.EVERNOTE;
import static gov.nasa.jpl.hi.marsimages.MarsImagesApp.MARS_IMAGES;
/**
* Created by mpowell on 4/5/15.
*/
public class ImageQuad extends Quad {
static final double x_axis[] = {1,0,0};
static final double z_axis[] = {0,0,1};
static final int numberOfPositions = 4;
private static final String TAG = "ImageQuad";
private final Number3D sphereVector;
private final double roverCameraFOVRadians;
private String cameraId = null;
private final int layer;
private double boundingSphereRadius;
private String imageId;
private final float[] center = new float[3];
private final Number3D mBoundsCenter;
public boolean isLoading() {
return loading;
}
private boolean loading = false;
private boolean cancelLoadingRequest = false;
private int originallyRequestedResolution = 0;
private Model model;
static final float textureCoords[] = {0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f};
public TextureInfo textureToAdd = null;
public ImageQuad(Model model, double[] qLL, String imageID) {
super();
this.imageId = imageID;
this.model = model;
String cameraId = MARS_IMAGES.getMission().getCameraId(imageID);
this.cameraId = cameraId;
roverCameraFOVRadians = MARS_IMAGES.getMission().getCameraFOV(cameraId);
int layer = 5 + MARS_IMAGES.getMission().getLayer(cameraId, imageID);
this.layer = layer;
float[][] vertices = new float[4][];
for (int i = 0; i < 4; i++) { vertices[i] = new float[3]; }
getImageVertices(model, qLL, vertices, layer);
System.arraycopy(vertices[0], 0, v0, 0, 3);
System.arraycopy(vertices[1],0,v1,0,3);
System.arraycopy(vertices[2],0,v2,0,3);
System.arraycopy(vertices[3],0,v3,0,3);
init();
center[0] = (v0[0]+v2[0])/2;
center[1] = (v0[1]+v2[1])/2;
center[2] = (v0[2]+v2[2])/2;
mBoundsCenter = new Number3D(center[0], center[1], center[2]);
sphereVector = new Number3D(mBoundsCenter);
sphereVector.normalize();
//assign to the radius the distance from the center to the farthest vertex
double d0 = distanceBetween(center, v0);
double d1 = distanceBetween(center, v1);
double d2 = distanceBetween(center, v2);
double d3 = distanceBetween(center, v3);
boundingSphereRadius = d0;
if (d1>boundingSphereRadius) boundingSphereRadius=d1;
if (d2>boundingSphereRadius) boundingSphereRadius=d2;
if (d3>boundingSphereRadius) boundingSphereRadius=d3;
}
public Number3D getBoundsCenter() {
return mBoundsCenter;
}
public double getBoundsRadius() {
return boundingSphereRadius;
}
private double cameraFOVRadians() {
if (cameraId != null)
return MARS_IMAGES.getMission().getCameraFOV(cameraId);
throw new IllegalStateException("roverCameraFOVRadians called before cameraId initialized");
}
private double distanceBetween(float[] pt1, float[] pt2) {
float dx = pt1[0]-pt2[0];
float dy = pt1[1]-pt2[1];
float dz = pt1[2]-pt2[2];
return Math.sqrt(dx*dx+dy*dy+dz*dz);
}
private void getImageVertices(Model model, double[] qLL, float[][] vertices, float distance) {
Rover mission = MARS_IMAGES.getMission();
double eye[] = new double[3];
double pos[] = new double[2], pos3[] = new double[3], vec3[] = new double[3];
double pos3LL[]= new double[3], pinitial[] = new double[3], pfinal[] = new double[3];
double xrotq[] = new double[4];
M.quatva(x_axis, Math.PI/2, xrotq);
double zrotq[] = new double[4];
M.quatva(z_axis, -Math.PI/2, zrotq);
double llRotq[] = new double[4];
eye[0] = mission.getMastX();
eye[1] = mission.getMastY();
eye[2] = mission.getMastZ();
llRotq[0] = qLL[0];
llRotq[1] = qLL[1];
llRotq[2] = qLL[2];
llRotq[3] = qLL[3];
//lower left
pos[0] = 0;
pos[1] = model.ydim();
model.cmod_2d_to_3d(pos, pos3, vec3);
pos3[0] -= eye[0];
pos3[1] -= eye[1];
pos3[2] -= eye[2];
pos3[0] += vec3[0]*distance;
pos3[1] += vec3[1]*distance;
pos3[2] += vec3[2]*distance;
M.multqv(llRotq, pos3, pos3LL);
M.multqv(zrotq, pos3LL, pinitial);
M.multqv(xrotq, pinitial, pfinal);
vertices[0][0] = (float)pfinal[0];
vertices[0][1] = (float)pfinal[1];
vertices[0][2] = (float)pfinal[2];
//upper left
pos[0] = 0;
pos[1] = 0;
model.cmod_2d_to_3d(pos, pos3, vec3);
pos3[0] -= eye[0];
pos3[1] -= eye[1];
pos3[2] -= eye[2];
pos3[0] += vec3[0]*distance;
pos3[1] += vec3[1]*distance;
pos3[2] += vec3[2]*distance;
M.multqv(llRotq, pos3, pos3LL);
M.multqv(zrotq, pos3LL, pinitial);
M.multqv(xrotq, pinitial, pfinal);
vertices[1][0] = (float)pfinal[0];
vertices[1][1] = (float)pfinal[1];
vertices[1][2] = (float)pfinal[2];
//lower right
pos[0] = model.xdim();
pos[1] = model.ydim();
model.cmod_2d_to_3d(pos, pos3, vec3);
pos3[0] -= eye[0];
pos3[1] -= eye[1];
pos3[2] -= eye[2];
pos3[0] += vec3[0]*distance;
pos3[1] += vec3[1]*distance;
pos3[2] += vec3[2]*distance;
M.multqv(llRotq, pos3, pos3LL);
M.multqv(zrotq, pos3LL, pinitial);
M.multqv(xrotq, pinitial, pfinal);
vertices[2][0] = (float)pfinal[0];
vertices[2][1] = (float)pfinal[1];
vertices[2][2] = (float)pfinal[2];
//upper right
pos[0] = model.xdim();
pos[1] = 0;
model.cmod_2d_to_3d(pos, pos3, vec3);
pos3[0] -= eye[0];
pos3[1] -= eye[1];
pos3[2] -= eye[2];
pos3[0] += vec3[0]*distance;
pos3[1] += vec3[1]*distance;
pos3[2] += vec3[2]*distance;
M.multqv(llRotq, pos3, pos3LL);
M.multqv(zrotq, pos3LL, pinitial);
M.multqv(xrotq, pinitial, pfinal);
vertices[3][0] = (float)pfinal[0];
vertices[3][1] = (float)pfinal[1];
vertices[3][2] = (float)pfinal[2];
}
public boolean cameraIsLookingAtMe(Camera viewPortCamera) {
Number3D cameraPointing = viewPortCamera.getLookAt();
double angleBetweenRadians = Math.acos(Number3D.dot(cameraPointing, sphereVector));
// return angleBetweenRadians - Math.toRadians(camera.getFieldOfView()) - roverCameraFOVRadians <= 0;
return angleBetweenRadians - roverCameraFOVRadians < Math.toRadians(viewPortCamera.getFieldOfView())*.7071;
}
public void loadImageAndTexture(final Note photo, final String title, final int resolution, final MarsMosaicRenderer renderer) {
if (photo == null) {
Log.e(TAG, "No photo in scene with title " + title);
return;
}
synchronized (this) {
cancelLoadingRequest = false;
if (loading) {
return;
}
loading = true;
final int bestTextureResolution = computeBestTextureResolution(resolution);
// Log.d(TAG, "Best texture resolution: "+bestTextureResolution);
originallyRequestedResolution = bestTextureResolution;
}
ImageLoader.getInstance().resume(); //in case the image loader engine is currently paused
final String uri = EVERNOTE.getUri(photo.getResources().get(0));
ImageLoader.getInstance().loadImage(uri, new ImageSize(originallyRequestedResolution, originallyRequestedResolution), new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, final Bitmap loadedImage) {
//if this image is no longer in the viewport, early out
synchronized (ImageQuad.this) {
if (!loading) {
return;
}
if (cancelLoadingRequest) {
loading = false;
cancelLoadingRequest = false;
return;
}
int width = loadedImage.getWidth();
int height = loadedImage.getHeight();
if (!getTextureInfoList().isEmpty()) {
int testWidth = width, testHeight = height;
if (testWidth != originallyRequestedResolution || testHeight != originallyRequestedResolution) {
testWidth = originallyRequestedResolution; testHeight = originallyRequestedResolution;
}
TextureInfo info = getTextureInfoList().get(0);
if (testWidth == info.getWidth() || testHeight == info.getHeight()) {
loading = false;
return;
}
}
// Log.d(TAG, "Loaded image size: " + width + "x" + height + " for image " + title);
final Bitmap texture = (width != originallyRequestedResolution || height != originallyRequestedResolution) ?
Bitmap.createScaledBitmap(loadedImage, originallyRequestedResolution, originallyRequestedResolution, true) : loadedImage;
if (!loading) return;
int bestTextureResolution = computeBestTextureResolution(resolution);
if (bestTextureResolution != originallyRequestedResolution) {
Log.d(TAG, "Texture resolution mismatch: "+bestTextureResolution + " vs. "+originallyRequestedResolution);
loading = false;
return;
}
renderer.getSurfaceView().queueEvent(new Runnable() {
@Override
public void run() {
Runnable runnableForGL = new Runnable() {
@Override
public void run() {
renderer.getTextureManager().removeTextures(getTextureInfoList());
for (TextureInfo textureInfo : getTextureInfoList())
removeTexture(textureInfo);
getTextureInfoList().clear();
final TextureInfo textureInfo = renderer.getTextureManager().addTexture(texture, TextureManager.TextureType.DIFFUSE, false, false);
setDrawingMode(GL_TRIANGLE_FAN);
setMaterial(new SimpleMaterial());
addTexture(textureInfo);
loading = false;
}
};
renderer.glRunnables.add(runnableForGL);
}
});
}
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
Log.e(TAG, "Loading cancelled for "+imageUri);
loading = false;
super.onLoadingCancelled(imageUri, view);
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
Log.e(TAG, "Loading failed for "+imageUri);
loading = false;
super.onLoadingFailed(imageUri, view, failReason);
}
});
}
private int computeBestTextureResolution(int resolution) {
int largestImageDimension = (int)Math.max(model.xdim(), model.ydim());
int bestImageResolution = Math.min(largestImageDimension, resolution);
// Log.d(TAG, "Texture res: " + M.floorPowerOfTwo(bestImageResolution));
return M.floorPowerOfTwo(bestImageResolution);
}
public void stopLoading() {
synchronized (this) {
cancelLoadingRequest = true;
}
}
}