Saturday, January 28, 2023

Sierpinski Gasket in an Android App

After making several apps which were not doing anything much but just to know the way android apps are created, sat down to create applications which might be a bit useful.

First of them, an android app that displays a sierpinski gasket.

Here is a snap:



This is the autogenerated MainActivity.java with a little modifications:

package com.example.mukesh.seirpinskigasketandroid;

import android.opengl.GLSurfaceView;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
Next up the MyGLSurfaceView.java class
package com.example.mukesh.seirpinskigasketandroid;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context) {
super(context);

// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);

mRenderer = new MyGLRenderer();

// Set the Renderer for drawing on the GLSurfaceView
setRenderer(mRenderer);
// Render the view only when there is a change in the drawing data
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}

Next the MyGlRenderer.java class
package com.example.mukesh.seirpinskigasketandroid;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyGLRenderer implements GLSurfaceView.Renderer {
private Triangle mTriangle;
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// initialize a triangle
mTriangle = new Triangle();
// initialize a square
}

@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}

@Override
public void onDrawFrame(GL10 gl10) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mTriangle.draw();
}
public static int loadShader(int type, String shaderCode){

// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);

// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);

return shader;
}
}
Next is the Triangle.java class
package com.example.mukesh.seirpinskigasketandroid;

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class Triangle {
private final int mProgram;
private FloatBuffer vertexBuffer;

// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[] = { // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
};
float v[][]={{-1.0f,-0.5f,0.0f},{1.0f,-0.5f,0.0f},
{0.0f,1.0f,0.0f}};
float colors[][]={{1.0f,0.0f,0.0f},{0.0f,1.0f,0.0f},{0.0f,0.0f,1.0f},{0.0f,0.0f,0.0f}};
int n=5;
int vc1=0;
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

public Triangle() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
10000);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());

// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
// add the coordinates to the FloatBuffer
//vertexBuffer.put(triangleCoords);
// set the buffer to read the first coordinate

divide_tetra(vertexBuffer,v[0],v[1],v[2],n);

vertexBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);

// create empty OpenGL ES Program
mProgram = GLES20.glCreateProgram();

// add the vertex shader to program
GLES20.glAttachShader(mProgram, vertexShader);

// add the fragment shader to program
GLES20.glAttachShader(mProgram, fragmentShader);

// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgram);
}
void triangle(FloatBuffer fb,float a[],float b[],float c[])
{

fb.put(a);

fb.put(b);

fb.put(c);
vc1++;
}


void divide_tetra(FloatBuffer fb,float a[],float b[],float c[],int m)
{
float v1[]=new float[3],v2[]=new float[3],v3[]=new float[3];
int j;
if(m>0)
{ /*compute three midpoints*/
for(j=0;j<3;j++)
v1[j]=(a[j]+b[j])/2;

for(j=0;j<3;j++)
v2[j]=(a[j]+c[j])/2;

for(j=0;j<3;j++)
v3[j]=(c[j]+b[j])/2;

divide_tetra(fb,a,v2,v1,m-1);
divide_tetra(fb,c,v3,v2,m-1);
divide_tetra(fb,b,v1,v3,m-1);

}
else
triangle(fb, a, b, c); //draw triangle at end of recursion//
}


private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";

private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private int mPositionHandle;
private int mColorHandle;

private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX *4; // 4 bytes per vertex

public void draw() {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);

// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);

// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);

// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);

// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0,vc1);

// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
Several things need to be done majorly:
Creating the run/Debug configurations and selecting the emulator device.
This is how the output looks like:

for n=4 the triangle subdivision is upto 3 levels and here is how it looks like:
for n=3 the subdivisions are:

Thank you!!
https://github.com/bmkamath2000/mukBoApps/tree/main/SeirpinskiGasketAndroid

1 comment:

  1. I've read your post and code, and I found it's a great resource for learning to code.
    game development

    ReplyDelete