mirror of
https://github.com/vanitasvitae/Spherical
synced 2024-11-22 12:22:08 +01:00
360 degree images now will be displayed somehow (at least they are)
This commit is contained in:
parent
179d3b7bd2
commit
7e7dde386d
3 changed files with 181 additions and 16 deletions
|
@ -1,6 +1,9 @@
|
||||||
package de.trac.spherical;
|
package de.trac.spherical;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
@ -17,6 +20,7 @@ import de.trac.spherical.parser.PhotoSphereMetadata;
|
||||||
import de.trac.spherical.parser.SphereParser;
|
import de.trac.spherical.parser.SphereParser;
|
||||||
|
|
||||||
import de.trac.spherical.rendering.Renderer;
|
import de.trac.spherical.rendering.Renderer;
|
||||||
|
import de.trac.spherical.rendering.SphereSurfaceView;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@ -25,18 +29,17 @@ public class MainActivity extends AppCompatActivity {
|
||||||
public static final String MIME_PHOTO_SPHERE = "application/vnd.google.panorama360+jpg";
|
public static final String MIME_PHOTO_SPHERE = "application/vnd.google.panorama360+jpg";
|
||||||
public static final String MIME_IMAGE = "image/*";
|
public static final String MIME_IMAGE = "image/*";
|
||||||
|
|
||||||
private TextView text;
|
private SphereSurfaceView surfaceView;
|
||||||
private GLSurfaceView surfaceView;
|
private Renderer renderer;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
|
||||||
//TODO: Remove later
|
// Initialize renderer and setup surface view.
|
||||||
text = (TextView) findViewById(R.id.hello_world);
|
surfaceView = new SphereSurfaceView(this);
|
||||||
surfaceView = (GLSurfaceView) findViewById(R.id.surface_view);
|
renderer = new Renderer(surfaceView);
|
||||||
surfaceView.setEGLContextClientVersion(2);
|
setContentView(surfaceView);
|
||||||
surfaceView.setRenderer(new Renderer());
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
|
@ -129,7 +132,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayPhotoSphere(InputStream inputStream, PhotoSphereMetadata metadata) {
|
private void displayPhotoSphere(InputStream inputStream, PhotoSphereMetadata metadata) {
|
||||||
//Please fill me!
|
renderer.setBitmap(BitmapFactory.decodeStream(inputStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,6 +140,6 @@ public class MainActivity extends AppCompatActivity {
|
||||||
* @param inputStream
|
* @param inputStream
|
||||||
*/
|
*/
|
||||||
private void displayFlatImage(InputStream inputStream) {
|
private void displayFlatImage(InputStream inputStream) {
|
||||||
|
Toast.makeText(this, "Not yet implemented", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package de.trac.spherical.rendering;
|
package de.trac.spherical.rendering;
|
||||||
|
|
||||||
|
|
||||||
import android.database.MatrixCursor;
|
import android.graphics.Bitmap;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.opengl.GLUtils;
|
||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ import javax.microedition.khronos.egl.EGLConfig;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
import static android.opengl.GLES20.*;
|
import static android.opengl.GLES20.*;
|
||||||
|
import static android.opengl.GLUtils.getEGLErrorString;
|
||||||
|
|
||||||
public class Renderer implements GLSurfaceView.Renderer {
|
public class Renderer implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
|
@ -65,6 +67,29 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
private int positionLocation;
|
private int positionLocation;
|
||||||
private int textureCoordinatesLocation;
|
private int textureCoordinatesLocation;
|
||||||
private int mvpLocation;
|
private int mvpLocation;
|
||||||
|
private int texLocation;
|
||||||
|
|
||||||
|
// Store texture.
|
||||||
|
private final int textureID [] = new int[1];
|
||||||
|
|
||||||
|
// Store bitmap for lazy loading.
|
||||||
|
private Bitmap bitmap = null;
|
||||||
|
|
||||||
|
// Store input handler instance to determine transformation.
|
||||||
|
private SphereSurfaceView surfaceView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Will be set as renderer of the specified surface view.
|
||||||
|
* @param surfaceView SurfaceView which will own the renderer
|
||||||
|
*/
|
||||||
|
public Renderer(SphereSurfaceView surfaceView) {
|
||||||
|
if(surfaceView == null)
|
||||||
|
throw new NullPointerException("SurfaceView must not be null");
|
||||||
|
|
||||||
|
this.surfaceView = surfaceView;
|
||||||
|
this.surfaceView.setEGLContextClientVersion(2);
|
||||||
|
this.surfaceView.setRenderer(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the frame.
|
* Draws the frame.
|
||||||
|
@ -72,8 +97,19 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
*/
|
*/
|
||||||
public void onDrawFrame(GL10 unused) {
|
public void onDrawFrame(GL10 unused) {
|
||||||
|
|
||||||
|
// Upload texture, if necessary.
|
||||||
|
if(bitmap != null) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureID[0]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
|
||||||
|
|
||||||
|
// Release bitmap for garbage collection.
|
||||||
|
bitmap = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Update transformation matrix.
|
// Update transformation matrix.
|
||||||
Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modlMatrix, 0);
|
Matrix.multiplyMM(mvpMatrix, 0, surfaceView.getRotationMatrix(), 0, modlMatrix, 0);
|
||||||
Matrix.multiplyMM(mvpMatrix, 0, projMatrix, 0, mvpMatrix, 0);
|
Matrix.multiplyMM(mvpMatrix, 0, projMatrix, 0, mvpMatrix, 0);
|
||||||
|
|
||||||
// Draw the frame.
|
// Draw the frame.
|
||||||
|
@ -87,12 +123,17 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
glVertexAttribPointer(textureCoordinatesLocation, 2, GL_FLOAT, false, 2*4, sphere.getTextureCoordinatesBuffer());
|
glVertexAttribPointer(textureCoordinatesLocation, 2, GL_FLOAT, false, 2*4, sphere.getTextureCoordinatesBuffer());
|
||||||
|
|
||||||
glUniformMatrix4fv(mvpLocation, 1, false, mvpMatrix, 0);
|
glUniformMatrix4fv(mvpLocation, 1, false, mvpMatrix, 0);
|
||||||
|
glUniform1i(texLocation, 0);
|
||||||
glDrawElements(GL_TRIANGLES, sphere.getIndexBuffer().capacity(), GL_UNSIGNED_SHORT, sphere.getIndexBuffer());
|
glDrawElements(GL_TRIANGLES, sphere.getIndexBuffer().capacity(), GL_UNSIGNED_SHORT, sphere.getIndexBuffer());
|
||||||
|
|
||||||
glDisableVertexAttribArray(textureCoordinatesLocation);
|
glDisableVertexAttribArray(textureCoordinatesLocation);
|
||||||
glDisableVertexAttribArray(positionLocation);
|
glDisableVertexAttribArray(positionLocation);
|
||||||
|
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
|
||||||
|
int error = glGetError();
|
||||||
|
if(error != GL_NO_ERROR)
|
||||||
|
Log.e("Renderer", "Error: " + getEGLErrorString(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +160,7 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
//TODO: (re)move tmp code
|
//TODO: (re)move tmp code
|
||||||
Matrix.setIdentityM(modlMatrix, 0);
|
Matrix.setIdentityM(modlMatrix, 0);
|
||||||
Matrix.translateM(modlMatrix, 0, 0, 0, 4.0f);
|
//Matrix.translateM(modlMatrix, 0, 0, 0, 4.0f);
|
||||||
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
|
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,16 +170,21 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
|
||||||
// Initialize sphere.
|
// Initialize sphere.
|
||||||
sphere = new Sphere(1.0f, 32, 32); // TODO: choose useful parameters.
|
sphere = new Sphere(10.0f, 32, 32); // TODO: choose useful parameters.
|
||||||
|
|
||||||
// Set OpenGL state.
|
// Set OpenGL state.
|
||||||
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_FRONT);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
// Build shader program.
|
// Build shader program.
|
||||||
programID = buildProgram(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER);
|
programID = buildProgram(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER);
|
||||||
|
|
||||||
|
// Generate texture.
|
||||||
|
glGenTextures(1, textureID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,9 +192,18 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
*/
|
*/
|
||||||
public void deinitialize() {
|
public void deinitialize() {
|
||||||
sphere = null;
|
sphere = null;
|
||||||
|
glDeleteTextures(1, textureID, 0);
|
||||||
glDeleteProgram(programID);
|
glDeleteProgram(programID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the image data of the given bitmap into the internal texture.
|
||||||
|
* @param bitmap Bitmap to be set as texture
|
||||||
|
*/
|
||||||
|
public void setBitmap(Bitmap bitmap) {
|
||||||
|
this.bitmap = bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a shader program given vertex and fragment shader soruce.
|
* Builds a shader program given vertex and fragment shader soruce.
|
||||||
* @param vertexSource The vertex shader source
|
* @param vertexSource The vertex shader source
|
||||||
|
@ -194,6 +249,10 @@ public class Renderer implements GLSurfaceView.Renderer {
|
||||||
if (mvpLocation == -1) {
|
if (mvpLocation == -1) {
|
||||||
throw new RuntimeException("Could not get uniform location for 'mvpMatrix'");
|
throw new RuntimeException("Could not get uniform location for 'mvpMatrix'");
|
||||||
}
|
}
|
||||||
|
texLocation = glGetUniformLocation(program, "tex");
|
||||||
|
if(texLocation == -1) {
|
||||||
|
throw new RuntimeException("Could not get uniform location for 'tex'");
|
||||||
|
}
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package de.trac.spherical.rendering;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.opengl.Matrix;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This SurfaceView implementation is the glue between the Renderer and any input event.
|
||||||
|
*/
|
||||||
|
public class SphereSurfaceView extends GLSurfaceView implements SensorEventListener {
|
||||||
|
|
||||||
|
public static boolean USE_TOUCH = true; // TODO: determine dynamically
|
||||||
|
|
||||||
|
private final float TOUCH_SCALE_FACTOR = 180.0f / 1080;
|
||||||
|
private float previousX;
|
||||||
|
private float previousY;
|
||||||
|
|
||||||
|
// The actual rotation matrix determined by user input.
|
||||||
|
private final float rotationMatrix [] = new float[16];
|
||||||
|
|
||||||
|
// The following fields are used as cache.
|
||||||
|
private final float tmpMatrix[] = new float[9];
|
||||||
|
private final float tmpVector[] = new float[3];
|
||||||
|
|
||||||
|
public SphereSurfaceView(Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
Matrix.setIdentityM(rotationMatrix, 0);
|
||||||
|
|
||||||
|
SensorManager manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
Sensor accelerometer = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);
|
||||||
|
manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
|
||||||
|
if(!USE_TOUCH)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
float x = event.getX();
|
||||||
|
float y = event.getY();
|
||||||
|
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
|
||||||
|
float dx = x - previousX;
|
||||||
|
float dy = y - previousY;
|
||||||
|
|
||||||
|
if (y > getHeight() / 2)
|
||||||
|
dx = dx * -1 ;
|
||||||
|
|
||||||
|
if (x < getWidth() / 2)
|
||||||
|
dy = dy * -1 ;
|
||||||
|
|
||||||
|
Matrix.rotateM(rotationMatrix, 0, dy * TOUCH_SCALE_FACTOR, 1.0f, 0.0f, 0.0f);
|
||||||
|
Matrix.rotateM(rotationMatrix, 0, dx * TOUCH_SCALE_FACTOR, 0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousX = x;
|
||||||
|
previousY = y;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor s, int arg1) {
|
||||||
|
// unused
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
|
||||||
|
if(USE_TOUCH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SensorManager.getRotationMatrixFromVector(tmpMatrix, event.values);
|
||||||
|
SensorManager.getOrientation(tmpMatrix, tmpVector);
|
||||||
|
|
||||||
|
for (int i = 0; i < tmpVector.length; i++)
|
||||||
|
tmpVector[i] = Math.round(Math.toDegrees(tmpVector[i]));
|
||||||
|
|
||||||
|
synchronized (rotationMatrix) {
|
||||||
|
Matrix.setRotateEulerM(rotationMatrix, 0, -tmpVector[0], -tmpVector[1], -tmpVector[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a matrix representing the devices rotation.
|
||||||
|
* This function is thread safe.
|
||||||
|
* @return rotation matrix according to device rotation
|
||||||
|
*/
|
||||||
|
public float [] getRotationMatrix() {
|
||||||
|
synchronized (rotationMatrix) {
|
||||||
|
return rotationMatrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue