mirror of
https://github.com/vanitasvitae/Spherical
synced 2024-11-22 12:22:08 +01:00
Add first revision of sphere rendering
This commit is contained in:
parent
1e689b6d46
commit
203ee1e1af
5 changed files with 420 additions and 8 deletions
|
@ -1,12 +1,12 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 26
|
compileSdkVersion 25
|
||||||
buildToolsVersion "26.0.1"
|
buildToolsVersion "25.0.3"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.trac.spherical"
|
applicationId "de.trac.spherical"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 26
|
targetSdkVersion 25
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
@ -24,7 +24,7 @@ dependencies {
|
||||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
})
|
})
|
||||||
compile 'com.android.support:appcompat-v7:26.+'
|
compile 'com.android.support:appcompat-v7:25.+'
|
||||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
package de.trac.spherical;
|
package de.trac.spherical;
|
||||||
|
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import de.trac.spherical.rendering.Renderer;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private GLSurfaceView surfaceView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
surfaceView = (GLSurfaceView) findViewById(R.id.surface_view);
|
||||||
|
surfaceView.setEGLContextClientVersion(2);
|
||||||
|
surfaceView.setRenderer(new Renderer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
224
app/src/main/java/de/trac/spherical/rendering/Renderer.java
Normal file
224
app/src/main/java/de/trac/spherical/rendering/Renderer.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
180
app/src/main/java/de/trac/spherical/rendering/Sphere.java
Normal file
180
app/src/main/java/de/trac/spherical/rendering/Sphere.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,10 +6,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="de.trac.spherical.MainActivity">
|
tools:context="de.trac.spherical.MainActivity">
|
||||||
|
|
||||||
<TextView
|
<android.opengl.GLSurfaceView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/surface_view"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:text="Hello World!"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
|
Loading…
Reference in a new issue