2017-09-13 01:11:40 +02:00
|
|
|
package de.trac.spherical.rendering;
|
|
|
|
|
|
|
|
|
2017-09-13 15:05:38 +02:00
|
|
|
import android.graphics.Bitmap;
|
2017-09-13 01:11:40 +02:00
|
|
|
import android.opengl.GLSurfaceView;
|
2017-09-13 15:05:38 +02:00
|
|
|
import android.opengl.GLUtils;
|
2017-09-13 01:11:40 +02:00
|
|
|
import android.opengl.Matrix;
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import javax.microedition.khronos.egl.EGLConfig;
|
|
|
|
import javax.microedition.khronos.opengles.GL10;
|
|
|
|
|
|
|
|
import static android.opengl.GLES20.*;
|
2017-09-13 15:05:38 +02:00
|
|
|
import static android.opengl.GLUtils.getEGLErrorString;
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
public class Renderer implements GLSurfaceView.Renderer {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default vertex shader.
|
|
|
|
*
|
|
|
|
* In: pos
|
|
|
|
* uvw
|
|
|
|
* Out: uv
|
|
|
|
*/
|
|
|
|
private static final String DEFAULT_VERTEX_SHADER =
|
|
|
|
"uniform mat4 mvpMatrix;\n" +
|
|
|
|
"attribute vec3 position;\n" +
|
|
|
|
"attribute vec2 textureCoordinates;\n" +
|
|
|
|
"varying vec2 uv;\n" +
|
|
|
|
"void main() {\n" +
|
|
|
|
" gl_Position = mvpMatrix * vec4(position, 1);\n" +
|
|
|
|
" uv = textureCoordinates;\n" +
|
|
|
|
"}\n";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default fragment shader.
|
|
|
|
*
|
|
|
|
* In: uv
|
|
|
|
* Out: sets fragment color
|
|
|
|
*/
|
|
|
|
private static final String DEFAULT_FRAGMENT_SHADER =
|
|
|
|
"varying vec2 uv;\n" +
|
|
|
|
"uniform sampler2D tex;\n" +
|
|
|
|
"void main() {\n" +
|
2017-09-13 01:35:34 +02:00
|
|
|
" gl_FragColor = texture2D(tex, uv);\n" +
|
2017-09-13 01:11:40 +02:00
|
|
|
"}\n";
|
|
|
|
|
|
|
|
// Store a sphere geometry as framework for the photo texture.
|
|
|
|
private Sphere sphere = null;
|
|
|
|
|
|
|
|
// Store projection matrix.
|
|
|
|
private float projMatrix [] = new float [16];
|
|
|
|
|
|
|
|
// Store modelview matrix.
|
|
|
|
private float modlMatrix [] = new float [16];
|
|
|
|
|
|
|
|
// Store view matrix.
|
|
|
|
private float viewMatrix [] = new float [16];
|
|
|
|
|
|
|
|
// Store the model view projection matrix.
|
|
|
|
private float mvpMatrix [] = new float [16];
|
|
|
|
|
|
|
|
// Store shader name.
|
|
|
|
private int programID;
|
|
|
|
|
|
|
|
// Store shader locations.
|
|
|
|
private int positionLocation;
|
|
|
|
private int textureCoordinatesLocation;
|
|
|
|
private int mvpLocation;
|
2017-09-13 15:05:38 +02:00
|
|
|
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);
|
|
|
|
}
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws the frame.
|
|
|
|
* @param unused unused
|
|
|
|
*/
|
|
|
|
public void onDrawFrame(GL10 unused) {
|
|
|
|
|
2017-09-13 15:05:38 +02:00
|
|
|
// 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);
|
2017-09-14 01:20:51 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2017-09-13 15:05:38 +02:00
|
|
|
|
|
|
|
// Release bitmap for garbage collection.
|
|
|
|
bitmap = null;
|
|
|
|
}
|
|
|
|
|
2017-09-13 01:11:40 +02:00
|
|
|
// Update transformation matrix.
|
2017-09-13 15:05:38 +02:00
|
|
|
Matrix.multiplyMM(mvpMatrix, 0, surfaceView.getRotationMatrix(), 0, modlMatrix, 0);
|
2017-09-13 01:11:40 +02:00
|
|
|
Matrix.multiplyMM(mvpMatrix, 0, projMatrix, 0, mvpMatrix, 0);
|
|
|
|
|
|
|
|
// Draw the frame.
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glUseProgram(programID);
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(positionLocation);
|
|
|
|
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, false, 3*4, sphere.getVertexBuffer());
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(textureCoordinatesLocation);
|
|
|
|
glVertexAttribPointer(textureCoordinatesLocation, 2, GL_FLOAT, false, 2*4, sphere.getTextureCoordinatesBuffer());
|
|
|
|
|
|
|
|
glUniformMatrix4fv(mvpLocation, 1, false, mvpMatrix, 0);
|
2017-09-13 15:05:38 +02:00
|
|
|
glUniform1i(texLocation, 0);
|
2017-09-14 01:20:51 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID[0]);
|
2017-09-13 01:11:40 +02:00
|
|
|
glDrawElements(GL_TRIANGLES, sphere.getIndexBuffer().capacity(), GL_UNSIGNED_SHORT, sphere.getIndexBuffer());
|
2017-09-14 01:20:51 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2017-09-13 01:11:40 +02:00
|
|
|
|
2017-09-13 01:35:34 +02:00
|
|
|
glDisableVertexAttribArray(textureCoordinatesLocation);
|
|
|
|
glDisableVertexAttribArray(positionLocation);
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
glUseProgram(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback called if surface changed.
|
|
|
|
*
|
|
|
|
* @param unused unused
|
|
|
|
* @param width new width of the surface
|
|
|
|
* @param height new height of the surface
|
|
|
|
*/
|
|
|
|
public void onSurfaceChanged(GL10 unused, int width, int height) {
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
float ratio = (float) width / height;
|
|
|
|
Matrix.perspectiveM(projMatrix, 0, 45.0f, ratio, 0.25f, 128.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback called if surface has been created.
|
|
|
|
*
|
|
|
|
* @param unused unused
|
|
|
|
* @param config surface configuration
|
|
|
|
*/
|
|
|
|
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
|
|
|
|
|
|
|
// Initialize sphere.
|
2017-09-13 15:05:38 +02:00
|
|
|
sphere = new Sphere(10.0f, 32, 32); // TODO: choose useful parameters.
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
// Set OpenGL state.
|
2017-09-14 01:20:51 +02:00
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
2017-09-13 01:11:40 +02:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_CULL_FACE);
|
2017-09-14 01:20:51 +02:00
|
|
|
glFrontFace(GL_CW);
|
2017-09-13 15:05:38 +02:00
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
// Build shader program.
|
|
|
|
programID = buildProgram(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER);
|
2017-09-13 15:05:38 +02:00
|
|
|
|
|
|
|
// Generate texture.
|
|
|
|
glGenTextures(1, textureID, 0);
|
2017-09-13 01:11:40 +02:00
|
|
|
|
2017-09-14 01:20:51 +02:00
|
|
|
// Initialize matrices.
|
2017-09-14 01:46:22 +02:00
|
|
|
Matrix.setRotateM(modlMatrix, 0, 90, 1.0f, 0.0f, 0.0f);
|
2017-09-14 01:20:51 +02:00
|
|
|
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
|
2017-09-13 01:11:40 +02:00
|
|
|
}
|
|
|
|
|
2017-09-13 15:05:38 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2017-09-13 01:11:40 +02:00
|
|
|
/**
|
|
|
|
* Builds a shader program given vertex and fragment shader soruce.
|
|
|
|
* @param vertexSource The vertex shader source
|
|
|
|
* @param fragmentSource The fragment shader source
|
|
|
|
* @return shader program
|
|
|
|
*/
|
|
|
|
private int buildProgram(String vertexSource, String fragmentSource) {
|
|
|
|
|
|
|
|
int vertexShader = buildShader(GL_VERTEX_SHADER, vertexSource);
|
|
|
|
if (vertexShader == 0) {
|
|
|
|
throw new RuntimeException("vertex shader could not be loaded");
|
|
|
|
}
|
|
|
|
|
|
|
|
int fragmentShader = buildShader(GL_FRAGMENT_SHADER, fragmentSource);
|
|
|
|
if (fragmentShader == 0) {
|
|
|
|
throw new RuntimeException("fragment shader could not be loaded");
|
|
|
|
}
|
|
|
|
|
|
|
|
int program = glCreateProgram();
|
|
|
|
if (program != 0) {
|
|
|
|
glAttachShader(program, vertexShader);
|
|
|
|
glAttachShader(program, fragmentShader);
|
|
|
|
glLinkProgram(program);
|
|
|
|
int[] linkStatus = new int[1];
|
|
|
|
glGetProgramiv(program, GL_LINK_STATUS, linkStatus, 0);
|
|
|
|
if (linkStatus[0] != GL_TRUE) {
|
|
|
|
Log.e("Shader", "Could not link program: ");
|
|
|
|
Log.e("Shader", glGetProgramInfoLog(program));
|
|
|
|
glDeleteProgram(program);
|
|
|
|
throw new RuntimeException("Could not link program");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
positionLocation = glGetAttribLocation(program, "position");
|
|
|
|
if (positionLocation == -1) {
|
|
|
|
throw new RuntimeException("Could not get attribute location for 'position'");
|
|
|
|
}
|
|
|
|
textureCoordinatesLocation = glGetAttribLocation(program, "textureCoordinates");
|
|
|
|
if (textureCoordinatesLocation == -1) {
|
|
|
|
throw new RuntimeException("Could not get attribute location for 'textureCoordinates'");
|
|
|
|
}
|
|
|
|
mvpLocation = glGetUniformLocation(program, "mvpMatrix");
|
|
|
|
if (mvpLocation == -1) {
|
|
|
|
throw new RuntimeException("Could not get uniform location for 'mvpMatrix'");
|
|
|
|
}
|
2017-09-13 15:05:38 +02:00
|
|
|
texLocation = glGetUniformLocation(program, "tex");
|
|
|
|
if(texLocation == -1) {
|
|
|
|
throw new RuntimeException("Could not get uniform location for 'tex'");
|
|
|
|
}
|
2017-09-13 01:11:40 +02:00
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds a shader of a specified type from a given source.
|
|
|
|
* @param type The shader type.
|
|
|
|
* @param source The shader source
|
|
|
|
* @return shader name
|
|
|
|
*/
|
|
|
|
private int buildShader(int type, String source) {
|
|
|
|
int shader = glCreateShader(type);
|
|
|
|
if (shader != 0) {
|
|
|
|
glShaderSource(shader, source);
|
|
|
|
glCompileShader(shader);
|
|
|
|
int[] compiled = new int[1];
|
|
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, compiled, 0);
|
|
|
|
if (compiled[0] == 0) {
|
|
|
|
Log.e("Shader", "Could not compile shader " + type + ":");
|
|
|
|
Log.e("Shader", glGetShaderInfoLog(shader));
|
|
|
|
glDeleteShader(shader);
|
|
|
|
shader = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
}
|