5 changed files with 420 additions and 8 deletions
@ -1,13 +1,21 @@
|
||||
package de.trac.spherical; |
||||
|
||||
import android.opengl.GLSurfaceView; |
||||
import android.support.v7.app.AppCompatActivity; |
||||
import android.os.Bundle; |
||||
|
||||
import de.trac.spherical.rendering.Renderer; |
||||
|
||||
public class MainActivity extends AppCompatActivity { |
||||
|
||||
private GLSurfaceView surfaceView; |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
setContentView(R.layout.activity_main); |
||||
surfaceView = (GLSurfaceView) findViewById(R.id.surface_view); |
||||
surfaceView.setEGLContextClientVersion(2); |
||||
surfaceView.setRenderer(new Renderer()); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,224 @@
|
||||
package de.trac.spherical.rendering; |
||||
|
||||
|
||||
import android.database.MatrixCursor; |
||||
import android.opengl.GLSurfaceView; |
||||
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.*; |
||||
|
||||
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 = |
||||
"precision mediump float;\n" + |
||||
"varying vec2 uv;\n" + |
||||
"uniform sampler2D tex;\n" + |
||||
"void main() {\n" + |
||||
" gl_FragColor = vec4(0);//texture2D(tex, uv);\n" + |
||||
"}\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; |
||||
|
||||
/** |
||||
* Draws the frame. |
||||
* @param unused unused |
||||
*/ |
||||
public void onDrawFrame(GL10 unused) { |
||||
|
||||
// Update transformation matrix.
|
||||
Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modlMatrix, 0); |
||||
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); |
||||
glDrawElements(GL_TRIANGLES, sphere.getIndexBuffer().capacity(), GL_UNSIGNED_SHORT, sphere.getIndexBuffer()); |
||||
|
||||
glDisableVertexAttribArray(0); |
||||
glDisableVertexAttribArray(1); |
||||
|
||||
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(); |
||||
|
||||
//TODO: (re)move tmp code
|
||||
Matrix.setIdentityM(modlMatrix, 0); |
||||
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); |
||||
} |
||||
|
||||
/** |
||||
* Initialize OpenGL state and data. |
||||
*/ |
||||
public void initialize() { |
||||
|
||||
// Initialize sphere.
|
||||
sphere = new Sphere(1.0f, 32, 32); // TODO: choose useful aparameters.
|
||||
|
||||
// Set OpenGL state.
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
||||
glEnable(GL_DEPTH_TEST); |
||||
glEnable(GL_CULL_FACE); |
||||
glCullFace(GL_BACK); |
||||
|
||||
// Build shader program.
|
||||
programID = buildProgram(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER); |
||||
} |
||||
|
||||
/** |
||||
* Reset OpenGL state and delete data. |
||||
*/ |
||||
public void deinitialize() { |
||||
sphere = null; |
||||
glDeleteProgram(programID); |
||||
} |
||||
|
||||
/** |
||||
* 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'"); |
||||
} |
||||
|
||||
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; |
||||
} |
||||
} |
@ -0,0 +1,180 @@
|
||||
package de.trac.spherical.rendering; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.nio.FloatBuffer; |
||||
import java.nio.ShortBuffer; |
||||
|
||||
/** |
||||
* This class is used to create native buffers holding vertices and |
||||
* texture coordinates of a sphere with a given radius. |
||||
*/ |
||||
public class Sphere { |
||||
|
||||
// The following attributes make up our sphere.
|
||||
private FloatBuffer vertexBuffer; |
||||
private FloatBuffer textureCoordinatesBuffer; |
||||
private ShortBuffer indexBuffer; |
||||
|
||||
public Sphere(float radius, int polyCountX, int polyCountY) { |
||||
|
||||
// Setup vertex buffer.
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect((polyCountX*polyCountY+2)*2*3*4); |
||||
buffer.order(ByteOrder.nativeOrder()); |
||||
vertexBuffer = buffer.asFloatBuffer(); |
||||
|
||||
// Setup texture coordinate buffer.
|
||||
buffer = ByteBuffer.allocateDirect((polyCountX*polyCountY+2)*2*2*4); |
||||
buffer.order(ByteOrder.nativeOrder()); |
||||
textureCoordinatesBuffer = buffer.asFloatBuffer(); |
||||
|
||||
// Setup index buffer.
|
||||
buffer = ByteBuffer.allocateDirect(polyCountX*polyCountY*6*2); |
||||
buffer.order(ByteOrder.nativeOrder()); |
||||
indexBuffer = buffer.asShortBuffer(); |
||||
|
||||
int polyCountXPitch = polyCountX+1; // get to same vertex on next level
|
||||
|
||||
int level = 0; |
||||
|
||||
for (int p1 = 0; p1 < polyCountY-1; p1++) { |
||||
//main quads, top to bottom
|
||||
for (int p2 = 0; p2 < polyCountX - 1; p2++) |
||||
{ |
||||
final int curr = level + p2; |
||||
indexBuffer.put((short)(curr + polyCountXPitch)); |
||||
indexBuffer.put((short)(curr)); |
||||
indexBuffer.put((short)(curr + 1)); |
||||
indexBuffer.put((short)(curr + polyCountXPitch)); |
||||
indexBuffer.put((short)(curr+1)); |
||||
indexBuffer.put((short)(curr + 1 + polyCountXPitch)); |
||||
} |
||||
|
||||
// the connectors from front to end
|
||||
indexBuffer.put((short)(level + polyCountX - 1 + polyCountXPitch)); |
||||
indexBuffer.put((short)(level + polyCountX - 1)); |
||||
indexBuffer.put((short)(level + polyCountX)); |
||||
|
||||
indexBuffer.put((short)(level + polyCountX - 1 + polyCountXPitch)); |
||||
indexBuffer.put((short)(level + polyCountX)); |
||||
indexBuffer.put((short)(level + polyCountX + polyCountXPitch)); |
||||
level += polyCountXPitch; |
||||
} |
||||
|
||||
final int polyCountSq = polyCountXPitch * polyCountY; // top point
|
||||
final int polyCountSq1 = polyCountSq + 1; // bottom point
|
||||
final int polyCountSqM1 = (polyCountY - 1) * polyCountXPitch; // last row's first vertex
|
||||
|
||||
for (int p2 = 0; p2 < polyCountX - 1; p2++) { |
||||
// create triangles which are at the top of the sphere
|
||||
|
||||
indexBuffer.put((short)(polyCountSq)); |
||||
indexBuffer.put((short)(p2 + 1)); |
||||
indexBuffer.put((short)(p2)); |
||||
|
||||
// create triangles which are at the bottom of the sphere
|
||||
|
||||
indexBuffer.put((short)(polyCountSqM1 + p2)); |
||||
indexBuffer.put((short)(polyCountSqM1 + p2 + 1)); |
||||
indexBuffer.put((short)(polyCountSq1)); |
||||
} |
||||
|
||||
// create final triangle which is at the top of the sphere
|
||||
|
||||
indexBuffer.put((short)(polyCountSq)); |
||||
indexBuffer.put((short)(polyCountX)); |
||||
indexBuffer.put((short)(polyCountX-1)); |
||||
|
||||
// create final triangle which is at the bottom of the sphere
|
||||
|
||||
indexBuffer.put((short)(polyCountSqM1 + polyCountX - 1)); |
||||
indexBuffer.put((short)(polyCountSqM1)); |
||||
indexBuffer.put((short)(polyCountSq1)); |
||||
|
||||
// calculate the angle which separates all points in a circle
|
||||
final double AngleX = 2.0 * Math.PI / polyCountX; |
||||
final double AngleY = Math.PI / polyCountY; |
||||
|
||||
int i=0; |
||||
double axz; |
||||
|
||||
// we don't start at 0.
|
||||
double ay = 0;//AngleY / 2;
|
||||
for (int y = 0; y < polyCountY; y++) { |
||||
ay += AngleY; |
||||
final double sinay = Math.sin(ay); |
||||
axz = 0; |
||||
|
||||
// calculate the necessary vertices without the doubled one
|
||||
for (int xz = 0; xz < polyCountX; xz++) |
||||
{ |
||||
float rx = (float) (radius * Math.cos(axz) * sinay); |
||||
float ry = (float) (radius * Math.cos(ay)); |
||||
float rz = (float) (radius * Math.sin(axz) * sinay); |
||||
|
||||
// calculate texture coordinates via sphere mapping
|
||||
// tu is the same on each level, so only calculate once
|
||||
float tu = 0.5f; |
||||
if (y==0) |
||||
{ |
||||
if (ry != -1.0f && ry != 1.0f) { |
||||
float len = (float) Math.sqrt(rx*rx + ry*ry + rz*rz); |
||||
tu = (float) (Math.acos(Math.max(Math.min(rx / len / sinay, 1.0), -1.0)) * 0.5 / Math.PI); |
||||
} |
||||
if (rz < 0.0f) |
||||
tu=1-tu; |
||||
} |
||||
else |
||||
tu = textureCoordinatesBuffer.get((i-polyCountXPitch)*2); |
||||
|
||||
vertexBuffer.put(rx); |
||||
vertexBuffer.put(ry); |
||||
vertexBuffer.put(rz); |
||||
textureCoordinatesBuffer.put(tu); |
||||
textureCoordinatesBuffer.put((float)(ay/Math.PI)); |
||||
|
||||
i++; |
||||
axz += AngleX; |
||||
} |
||||
// This is the doubled vertex on the initial position
|
||||
vertexBuffer.put(vertexBuffer.get((i-polyCountX)*3 + 0)); |
||||
vertexBuffer.put(vertexBuffer.get((i-polyCountX)*3 + 1)); |
||||
vertexBuffer.put(vertexBuffer.get((i-polyCountX)*3 + 2)); |
||||
textureCoordinatesBuffer.put(1.0f); |
||||
textureCoordinatesBuffer.put(0.0f); |
||||
i++; |
||||
} |
||||
|
||||
// Add the vertex at the top of the sphere.
|
||||
vertexBuffer.put(0.0f); |
||||
vertexBuffer.put(radius); |
||||
vertexBuffer.put(0.0f); |
||||
textureCoordinatesBuffer.put(0.5f); |
||||
textureCoordinatesBuffer.put(0.0f); |
||||
|
||||
// Add the vertex at the bottom of the sphere.
|
||||
vertexBuffer.put(0.0f); |
||||
vertexBuffer.put(-radius); |
||||
vertexBuffer.put(0.0f); |
||||
textureCoordinatesBuffer.put(0.5f); |
||||
textureCoordinatesBuffer.put(1.0f); |
||||
|
||||
// Rewind buffers.
|
||||
vertexBuffer.position(0); |
||||
textureCoordinatesBuffer.position(0); |
||||
indexBuffer.position(0); |
||||
} |
||||
|
||||
public FloatBuffer getVertexBuffer() { |
||||
return vertexBuffer; |
||||
} |
||||
|
||||
public FloatBuffer getTextureCoordinatesBuffer() { |
||||
return textureCoordinatesBuffer; |
||||
} |
||||
|
||||
public ShortBuffer getIndexBuffer() { |
||||
return indexBuffer; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue