An overview of the

This is a new series, learn OpengGl Es, actually is “OpenGl Es Application Development Practice Guide Android volume” study notes, interested can directly read this book, of course, this will record their own understanding, the following only as notes, in case you forget later

The first nine chapters of the book will then be analyzed in turn

Android OpenGl Es learning (a) : create an OpenGl Es program

Android OpenGl Es learning (2) : define vertices and shaders

Android OpenGl Es learning (3) : build shaders

Android OpenGl Es learning (4) : add color

Android OpenGl Es learning (5) : adjust the aspect ratio

Android OpenGl Es Learning (vi) : Enter 3d

Android OpenGl Es learning (7) : Using textures

Android OpenGl Es Learning (8) : Build simple objects

The first article of the New Year

This chapter is to complete the effect directly above

A circle

We already know the drawing method of OpengL triangle fan. The circle can be regarded as a triangle fan with the center of the circle, as shown in the figure below:

The inside of a circle is a regular polygon, and when our regular polygon has enough sides (or triangles), it looks like a circle to the naked eye

Suppose our center is (x, y), and the radius of the circle is r, all we need to calculate is the coordinates of the surrounding points

We can easily calculate the coordinates of point A:

The x-coordinate of A is x plus r times cosine theta and the y-coordinate of A is y plus r times sin theta

If we divide the circle into n points, we can get the value of each Angle, and a for loop will get all the coordinates

Look at the code

    /** * Generates vertex data for the circle **@returnReturns the vertex coordinates of the circle */
    public float[] getVertexData() {
        // Divide into count triangles, one repeating vertex and one center vertex, so add 2
        int nodeCount = count + 2;
        // A container for storing vertex data
        float[] vertexData = new float[nodeCount * POSITION_COMPONENT_COUNT];

        int offset = 0;

        vertexData[offset++] = x;
        vertexData[offset++] = y;

        for (int i = 0; i < count + 1; i++) {
            float angleInRadians = ((float) i / (float) count)
                    * ((float) Math.PI * 2f);
            vertexData[offset++] = x + r * (float) Math.cos(angleInRadians);
            vertexData[offset++] = y + r * (float) Math.sin(angleInRadians);
        }

        return vertexData;

    }
Copy the code

If we split it into count triangles, then the number of vertices we need is count plus 2. If we split it into count triangles, we need one repeating vertex and one center vertex, so we need to add 2

And then you compute the angles, you compute the coordinates of each point

Let’s take a look at the complete code

public class Circle {

    // The number of bytes per vertex (x and y)
    private static final int POSITION_COMPONENT_COUNT = 2;
    // Each vertex takes up 4 bytes
    private static final int BYTES_PER_FLOAT = 4;
    private FloatBuffer floatBuffer;
    private int program;
    private int a_position;
    private int u_matrix;
    private float[] mProjectionMatrix = new float[16];

    // Center x coordinates
    private float x = 0;
    // The center of the circle is y
    private float y = 0;
    / / circle radius
    private float r = 0.6 f;
    // Number of triangles
    private int count = 50;
    private int u_color;


    public void init(Context context) {
        //1 generates vertices
        float[] vertexData = getVertexData();
        //2 Loads vertices into local memory
        initVertexData(vertexData);
        //3 Load the shader source code and load the program
        loadShaderAndProgram(context);
        //4 Loads the attributes in the shader
        loadShaderAttributes();
        //5 Bind shader properties to vertex data to enable the use of vertices
        bindAttributes();
    }

    /** * Generates vertex data for the circle **@returnReturns the vertex coordinates of the circle */
    public float[] getVertexData() {
        // Divide into count triangles, one repeating vertex and one center vertex, so add 2
        int nodeCount = count + 2;
        // A container for storing vertex data
        float[] vertexData = new float[nodeCount * POSITION_COMPONENT_COUNT];

        int offset = 0;

        vertexData[offset++] = x;
        vertexData[offset++] = y;

        for (int i = 0; i < count + 1; i++) {
            float angleInRadians = ((float) i / (float) count)
                    * ((float) Math.PI * 2f);
            vertexData[offset++] = x + r * (float) Math.cos(angleInRadians);
            vertexData[offset++] = y + r * (float) Math.sin(angleInRadians);
        }

        return vertexData;

    }

    /** * load the vertex data into local memory **@paramVertexData specifies the vertexData */
    public void initVertexData(float[] vertexData) {
        floatBuffer = ByteBuffer
                .allocateDirect(vertexData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);

        floatBuffer.position(0);
    }


    /** * loads the source of the shader and loads the program */
    public void loadShaderAndProgram(Context context) {
        // Read the shader source code
        String fragment_shader_source = ReadResouceText.readResoucetText(context, R.raw.simple_fragment_shader);
        String vertex_shader_source = ReadResouceText.readResoucetText(context, R.raw.simple_vertex_shader);

        // Compile the shader source code
        int mVertexshader = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
        int mFragmentshader = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);

        // Link program
        program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);

        // Validate opengL objects
        ShaderHelper.volidateProgram(program);

        // Use the program
        GLES20.glUseProgram(program);

    }


    /** * loads the attribute */ in the shader
    public void loadShaderAttributes(a) {
        // Get the shader attribute
        u_color = GLES20.glGetUniformLocation(program, "u_Color");
        a_position = GLES20.glGetAttribLocation(program, "a_Position");
        u_matrix = GLES20.glGetUniformLocation(program, "u_Matrix");
    }


    /** * bind the shader properties to the vertex data and enable the use of vertex */
    public void bindAttributes(a) {
        // Bind a_position and verticeData vertex positions
        / * * * the first parameter, the second parameter is the shader properties * and how much weight each vertex, we have only to the component * the third parameter, the fourth parameter data types * and is only meaningful plastic, ignore * 5 parameters, an array has multiple attributes to be meaningful, we only have an attribute, Pass 0 * sixth argument, where does OpengL read from */
        floatBuffer.position(0);
        GLES20.glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false.0, floatBuffer);
        // Start vertex
        GLES20.glEnableVertexAttribArray(a_position);
    }


    /** * Create an orthogonal matrix based on the width and height of the screen@paramWidth Screen width *@paramHeight Screen height */
    public void projectionMatrix(int width, int height) {
        float a = width > height ? (float) width / (float) height : (float) height / (float) width;

        if (width > height) {
            Matrix.orthoM(mProjectionMatrix, 0, -a, a, -1f.1f, -1f.1f);
        } else {
            Matrix.orthoM(mProjectionMatrix, 0, -1f.1f, -a, a, -1f.1f); }}/** * start drawing circles */
    public void draw(a) {
        // Set the circle's color to red
        GLES20.glUniform4f(u_color, 1.0 f.1.0 f.1.0 f.1f);
        // Set the matrix data
        GLES20.glUniformMatrix4fv(u_matrix, 1.false, mProjectionMatrix, 0);


        /** * First argument: draw draw triangle * second argument: read from vertex array 0 index * third argument: read several vertices ** finally draw circle */
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, count + 2); }}Copy the code
public class AirHockKeyRenderCircle implements GLSurfaceView.Renderer {
    private final Context mContext;
    private Circle circle;
    private Cylinder cylinder;

    / / draw circles


    public AirHockKeyRenderCircle(Context context) {
        this.mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GlsurfaceView calls this method when the surface is created. This happens in the application
        It is also called when you first run it or when you return from another Activity

        // Clear the screen
        GLES20.glClearColor(0.0 f.0.0 f.0.0 f.0.0 f);

        circle = new Circle();
        circle.init(mContext);

// cylinder = Cylinder.getInstance();
// cylinder.init(mContext);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // After the Surface is created, this method is called every time the Surface size changes, such as switching between horizontal and vertical screens
        // Set the screen size
        GLES20.glViewport(0.0, width, height);
// cylinder.projectionMatrix(width, height);
        circle.projectionMatrix(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // When drawing each frame of data, this method is called. This method must draw something, even if it is just to empty the screen
        // Because when this method returns, the data in the render area will be exchanged and displayed on the screen. If nothing else, you will see a flicker effect

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

// cylinder.draw();circle.draw(); }}Copy the code
/////simple_fragment_shader.glsl
 precision mediump float;
 uniform vec4 u_Color;
   void main() {
        gl_FragColor = u_Color;
    }
Copy the code
/////simple_vertex_shader.glsl
 attribute vec4 a_Position;

 uniform mat4 u_Matrix;

  void main() {
      gl_Position =  u_Matrix * a_Position;
   }
Copy the code

Look at the effect

Summarize the opengL writing process

  • The first step is to generate the vertex data
  • Loads vertices into local memory
  • Load the source of the shader and load the program
  • Loads the properties in the shader
  • Assign a value to a shader property
  • Start painting

Drawing cylinder

To build a cylinder we first split the cylinder, as shown below:

As you can see, a cylindrical expansion is really just two circles and a rectangle, and the drawing of circles has been described above

To construct the sides of a cylinder, we can use a related concept, which is called a triangle strip, and a triangle strip can define multiple triangles without having to write the same points in the triangle over and over again, and the triangles in the triangle strip are placed next to each other

The first three vertices of the strip define the first triangle, and then each vertex defines another triangle. To use the strip to define the sides of the cylinder, we simply roll the strip into a tube and make sure that the last two vertices are the same as the first two

Add point circle cylinder object

public class Geometry {

    / / points
    public static class Point {
        public final float x, y, z;

        public Point(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public Point translateY(float distance) {
            return newPoint(x, y + distance, z); }}/ / round
    public static class Circle {
        public final Point center;
        public final float radius;

        public Circle(Point center, float radius) {
            this.center = center;
            this.radius = radius;
        }

        public Circle scale(float scale) {
            return newCircle(center, radius * scale); }}/ / cylindrical
    public static class Cylinder {
        public final Point center;
        public final float radius;
        public final float height;

        public Cylinder(Point center, float radius, float height) {
            this.center = center;
            this.radius = radius;
            this.height = height; }}}Copy the code

Let’s start drawing a cylinder

 private static int sizeOfCricleInVerTices(int number) {
        // Divide into a number of triangles, requiring one repeating vertex and one center vertex, so add 2
        return 1 + number + 1;
    }


    private static int sizeOfCylinderInVerTices(int number) {
        // For each vertex around the top circle, there are two vertices, and the first two vertices need to be repeated twice to close
        return (number + 1) * 2;
    }
Copy the code

The side of the cylinder, which is a rolled rectangle, needs two vertices around each vertex of the top circle (the vertex of the upper circle and the vertex of the lower circle, which are the same in x and Z but different in Y), and the first two vertices need to be repeated twice to close


    // Create a column
    public void createPuck(Geometry.Cylinder puck, int number) {

        // Count the number of vertices needed to draw the cylinder
        int size = sizeOfCricleInVerTices(number) * 2 + sizeOfCylinderInVerTices(number);

        vertextData = new float[size * POSITION_COMPONENT_COUNT];
        // Create top circle
        Geometry.Circle puckTop = new Geometry.Circle(puck.center.translateY(puck.height/2), puck.radius);
        // Create bottom circle
        Geometry.Circle puckTop1 = new Geometry.Circle(puck.center.translateY(-puck.height/2), puck.radius);

        // Draw the side
        appendCylinder(puck, number);

        // Draw the top circle
        appendCircle(puckTop, number, true);
        // Draw the bottom circle
        appendCircle(puckTop1, number, false);

    }
Copy the code

So let’s calculate the number of vertices we need, we need two circles so times 2

Then create the top and bottom circles, whose centers are the cylinder’s center and the Y-axis plus or minus half the height of the cylinder

Then draw the sides, and finally the top and bottom circles

Construct circles with triangular fans

 private void appendCircle(Geometry.Circle circle, int number, final boolean color) {

        final int startVertex = offerset / FLOATS_PER_VERTEX;
        final int numberVertices = sizeOfCricleInVerTices(number);

        vertextData[offerset++] = circle.center.x;
        vertextData[offerset++] = circle.center.y;
        vertextData[offerset++] = circle.center.z;

        for (int i = 0; i <= number; i++) {
            // Calculate the Angle of each central Angle
            float angle = ((float) i / (float) number) * ((float) Math.PI * 2f);

            vertextData[offerset++] = circle.center.x + circle.radius * (float) Math.cos(angle);
            vertextData[offerset++] = circle.center.y;
            vertextData[offerset++] = circle.center.z + circle.radius * (float) Math.sin(angle);
// log.d ("mmm1 last ", offerset + "/");

        }

        Log.d("mmm1", startVertex + "/" + numberVertices + color);

        drawList.add(new DrawCommand() {
            @Override
            public void draw(a) {
                if (color) {
                    GLES20.glUniform4f(u_color, 0.0 f.1.0 f.0.0 f.1f);
                } else {
                    GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1f);
                }
                // Draw with a triangular fanGLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, startVertex, numberVertices); }}); }Copy the code

This is a little bit different from the above, which is that the top is a two-dimensional plane, this is a circle in a three-dimensional plane, and this circle is in the x and z plane, so let’s calculate it in terms of x and z

Finally, paint with a triangular fan

Draw the sides of the cylinder with triangular strips

 public void appendCylinder(Geometry.Cylinder cylinder, int number) {
        final int startVertex = offerset / FLOATS_PER_VERTEX;
        Log.d("What's going on with MMM1?", offerset + "/");
        final int numberVertices = sizeOfCylinderInVerTices(number);
        final float yStart = cylinder.center.y - cylinder.height / 2;
        final float yEed = cylinder.center.y + cylinder.height / 2;

        for (int i = 0; i <= number; i++) {
            float angle = ((float) i / (float) number) * ((float) Math.PI * 2f);

            float xPosition = cylinder.center.x + cylinder.radius * (float) Math.cos(angle);
            float zPosition = cylinder.center.z + cylinder.radius * (float) Math.sin(angle);

            vertextData[offerset++] = xPosition;
            vertextData[offerset++] = yStart;
            vertextData[offerset++] = zPosition;

            vertextData[offerset++] = xPosition;
            vertextData[offerset++] = yEed;
            vertextData[offerset++] = zPosition;

        }

        Log.d("mmm2", startVertex + "/" + numberVertices);

        drawList.add(new DrawCommand() {
            @Override
            public void draw(a) {
                GLES20.glUniform4f(u_color, 1.0 f.1.0 f.1.0 f.1f); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, startVertex, numberVertices); }}); }Copy the code

In fact, every time we compute it, we have two vertices, one at the top and one at the bottom, and they have the same x and z, but different y, so let’s compute the y of the top and the y of the bottom,YEnd and yStart

The next generation uses the same algorithm as above to generate the circle, except that for every point we generate, we assign two vertices, one is the top of the circle and one is the bottom of the circle. The first two points are repeated twice to make the circle close

Add camera (View Matrix)

We also need to add the concept of a camera through the view matrix

We don’t have any matrix from the earliest start, and then add the aspect ratio orthogonal matrix to solve the problem, then switch to the matrix to obtain a three-dimensional perspective projection, then add an object model matrix started moving back and forth, just an extension of the matrix model view matrix, they for the same purpose, but the view of equality of matrix is applied to every object in the scene

Simple matrix hierarchy

  • The model of matrix

He can put objects in the world coordinate system, such as we have a cylindrical model and cube model, their initial center at (0, 0), there is no model matrix where they will card, if we want to move him, we have to update each vertex of the matrix, if you don’t want to do this, you can use the model of matrix, the vertex and matrix multiplication, To change their position

  • The view matrix

The view matrix is used for the same reason as the model matrix, it affects every object in the scene, because it affects all objects, so it is functionally like a camera, move the camera back and forth, you see those things from a different perspective

  • Projection matrix

This matrix helps create 3d fantasies

How does a vertex move from its original position to the screen

  • vertexmodel

This is a vertex in the model coordinate system, which is the vertex data that we defined in the code

  • vertexworld

This is a vertex positioned with the model matrix, the world coordinate system

  • vertexeye

This is a vertex corresponding to our eye or camera that we can get using the view matrix

  • vertexclip

This is the vertex processed by the projection matrix, and the next step is to do perspective division

  • vertexndc

This is a vertex in a normalized coordinate, and once a vertex falls into this coordinate, OpengL will map it to the window, and you’ll see it on the screen

And we can actually see that we can manipulate these three matrices, and everything else is done automatically by the system

Initialize the view matrix

 /** * Create an orthogonal matrix based on the width and height of the screen@paramWidth Screen width *@paramHeight Screen height */
    public void projectionMatrix(int width, int height) {

        // Create a perspective projection that starts at z-1 and ends at -10
        MatrixHelper.perspetiveM(mProjectionMatrix, 45, (float) width / (float) height, 1f.10f);

        // Create view matrix
        Matrix.setLookAtM(mViewMatrix, 0.0f.2f.2f.0f.0f.0f.0f.1f.0f);
    }
Copy the code

Take a look at the method API in detail

  public static void setLookAtM(float[] rm, int rmOffset,
            float eyeX, float eyeY, float eyeZ,
            float centerX, float centerY, float centerZ, float upX, float upY,
            float upZ) {

Copy the code
parameter introduce
float[] rm This is the target array, which contains at least 16 elements so that it can store the attempt matrix
int rmOffset Will be stored to RM from this offset
float eyeX, float eyeY, float eyeZ This is where the eyes are, and everything in the scene looks at him from this point
float centerX, float centerY, float centerZ This is where the eyes are looking, and this position is in the center of the scene
float upX, float upY,float upZ We’re talking about your eyes, this is where your head is pointing, upY=1 means your head is pointing straight up

The top parameter sets the eye position to (0f, 2f, 2f), which means that the eye position is 2f above the x-z plane, and moves back 2 units, which means that all objects in the scene appear 2 units below you and 2 units in front of you. Set center to (0,0,0), which means that you are looking down towards the origin. And setting up to (0,1,0) means your head is straight up and the scene doesn’t rotate to either side

painting

  public void draw(a) {
        // Matrix multiplication
        Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
        positionTableInScreen();
        GLES20.glUniformMatrix4fv(u_matrix, 1.false, mViewModelProjectionMatrix, 0);

// Log.d("mmm", drawList.size() + "/");
        for(DrawCommand command : drawList) { command.draw(); }}private void positionTableInScreen(a) {
        // Matrix multiplication
        Matrix.multiplyMM(mViewModelProjectionMatrix, 0, mViewProjectionMatrix,
                0, mModelMatrix, 0);

    }
Copy the code

This is easy, just multiply all the matrices you need to get to the final matrix, and then draw the cylinder at the beginning

Add touch feedback

It’s pretty much the same as touch feedback in Android

glSurfaceView = findViewById(R.id.glsurface);
        glSurfaceView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float x = event.getX();// The current touch position X coordinates
                float y = event.getY();// The current touch position X coordinates

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
// Cylinder.getInstance().rotate(10, 1, 0, 0);
/ / Cylinder. The getInstance (). Scale (0.6 0.4 f, 1.5 f, f); // The xyz three directions are scaled according to their respective scaling factors

                        break;
                    case MotionEvent.ACTION_MOVE:// When a movement event is detected
                        float dx = x - mPreviousX;
                        float dy = y - mPreviousY;

                        if (dx > 0) {
                            Cylinder.getInstance().rotate(-dx, 1.0.0);
/ / Cylinder. The getInstance (). The translate (0.1 f, 0, 0);
                        } else  {
                            Cylinder.getInstance().rotate(dx, 1.0.0);
/ / Cylinder. The getInstance (). The translate (0.1 f, 0, 0);
                        }

                        if (dy > 0) {
                            Cylinder.getInstance().rotate(-dy, 0.0.1);
                        } else  {
                            Cylinder.getInstance().rotate(dy, 0.0.1);

                        }
                        break;
                }
                mPreviousX = x;
                mPreviousY = y;

                return true; }});Copy the code

Just add a setOnTouchListener and listen for ACTION_MOVE events, and then do your own actions, such as here, to rotate the cylinder while moving. After running, this is what the first image in this article looks like

The complete code

public class Cylinder {

    // The number of bytes per vertex (x and y)
    private static final int POSITION_COMPONENT_COUNT = 3;
    // Each vertex takes up 4 bytes
    private static final int BYTES_PER_FLOAT = 4;
    public static final int FLOATS_PER_VERTEX = 3;
    private FloatBuffer floatBuffer;
    private int program;
    private int a_position;
    private int u_matrix;
    private float[] mProjectionMatrix = new float[16];
    private float[] mModelMatrix = new float[16];
    // View matrix
    private float[] mViewMatrix = new float[16];

    private float[] mViewProjectionMatrix = new float[16];

    private float[] mViewModelProjectionMatrix = new float[16];

    private static Cylinder cylinder = new Cylinder();


    // Center x coordinates
    private float x = 0;
    // The center of the circle is y
    private float y = 0;
    / / circle radius
    private float r = 0.6 f;
    // Number of triangles
    private int count = 50;
    private int u_color;
    private int offerset;
    private float[] vertextData;

    private ArrayList<DrawCommand> drawList = new ArrayList<>();

    private Cylinder(a) {}public static Cylinder getInstance(a) {
        return cylinder;
    }

    public void init(Context context) {
        // Set to the identity matrix
        Matrix.setIdentityM(mModelMatrix, 0);
        //1 generates vertices
        //2 Loads vertices into local memory
        Geometry.Point point = new Geometry.Point(0f.0f.0f);
        Geometry.Cylinder cylinder = new Geometry.Cylinder(point, 0.4 f.0.5 f);
        createPuck(cylinder, 50);
        initVertexData(vertextData);
        //3 Load the shader source code and load the program
        loadShaderAndProgram(context);
        //4 Loads the attributes in the shader
        loadShaderAttributes();
        //5 Bind shader properties to vertex data to enable the use of vertices
        bindAttributes();
    }


    private static int sizeOfCricleInVerTices(int number) {
        // Divide into a number of triangles, requiring one repeating vertex and one center vertex, so add 2
        return 1 + number + 1;
    }


    private static int sizeOfCylinderInVerTices(int number) {
        // For each vertex around the top circle, there are two vertices, and the first two vertices need to be repeated twice to close
        return (number + 1) * 2;
    }

    // Create a column
    public void createPuck(Geometry.Cylinder puck, int number) {

        // Count the number of vertices needed to draw the cylinder
        int size = sizeOfCricleInVerTices(number) * 2 + sizeOfCylinderInVerTices(number);

        vertextData = new float[size * POSITION_COMPONENT_COUNT];
        // Create top circle
        Geometry.Circle puckTop = new Geometry.Circle(puck.center.translateY(puck.height/2), puck.radius);
        // Create bottom circle
        Geometry.Circle puckTop1 = new Geometry.Circle(puck.center.translateY(-puck.height/2), puck.radius);

        // Draw the side
        appendCylinder(puck, number);

        // Draw the top circle
        appendCircle(puckTop, number, true);
        // Draw the bottom circle
        appendCircle(puckTop1, number, false);

    }

    private void appendCircle(Geometry.Circle circle, int number, final boolean color) {

        final int startVertex = offerset / FLOATS_PER_VERTEX;
        final int numberVertices = sizeOfCricleInVerTices(number);

        vertextData[offerset++] = circle.center.x;
        vertextData[offerset++] = circle.center.y;
        vertextData[offerset++] = circle.center.z;

        for (int i = 0; i <= number; i++) {
            // Calculate the Angle of each central Angle
            float angle = ((float) i / (float) number) * ((float) Math.PI * 2f);

            vertextData[offerset++] = circle.center.x + circle.radius * (float) Math.cos(angle);
            vertextData[offerset++] = circle.center.y;
            vertextData[offerset++] = circle.center.z + circle.radius * (float) Math.sin(angle);
// log.d ("mmm1 last ", offerset + "/");

        }

        Log.d("mmm1", startVertex + "/" + numberVertices + color);

        drawList.add(new DrawCommand() {
            @Override
            public void draw(a) {
                if (color) {
                    GLES20.glUniform4f(u_color, 0.0 f.1.0 f.0.0 f.1f);
                } else {
                    GLES20.glUniform4f(u_color, 1.0 f.0.0 f.0.0 f.1f); } GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, startVertex, numberVertices); }}); }public void appendCylinder(Geometry.Cylinder cylinder, int number) {
        final int startVertex = offerset / FLOATS_PER_VERTEX;
        Log.d("What's going on with MMM1?", offerset + "/");
        final int numberVertices = sizeOfCylinderInVerTices(number);
        final float yStart = cylinder.center.y - cylinder.height / 2;
        final float yEed = cylinder.center.y + cylinder.height / 2;

        for (int i = 0; i <= number; i++) {
            float angle = ((float) i / (float) number) * ((float) Math.PI * 2f);

            float xPosition = cylinder.center.x + cylinder.radius * (float) Math.cos(angle);
            float zPosition = cylinder.center.z + cylinder.radius * (float) Math.sin(angle);

            vertextData[offerset++] = xPosition;
            vertextData[offerset++] = yStart;
            vertextData[offerset++] = zPosition;

            vertextData[offerset++] = xPosition;
            vertextData[offerset++] = yEed;
            vertextData[offerset++] = zPosition;

        }

        Log.d("mmm2", startVertex + "/" + numberVertices);

        drawList.add(new DrawCommand() {
            @Override
            public void draw(a) {
                GLES20.glUniform4f(u_color, 1.0 f.1.0 f.1.0 f.1f); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, startVertex, numberVertices); }}); }/** * load the vertex data into local memory **@paramVertexData specifies the vertexData */
    public void initVertexData(float[] vertexData) {
        floatBuffer = ByteBuffer
                .allocateDirect(vertexData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);

        floatBuffer.position(0);
    }


    /** * loads the source of the shader and loads the program */
    public void loadShaderAndProgram(Context context) {
        // Read the shader source code
        String fragment_shader_source = ReadResouceText.readResoucetText(context, R.raw.simple_fragment_shader);
        String vertex_shader_source = ReadResouceText.readResoucetText(context, R.raw.simple_vertex_shader);

        // Compile the shader source code
        int mVertexshader = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertex_shader_source);
        int mFragmentshader = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragment_shader_source);

        // Link program
        program = ShaderHelper.linkProgram(mVertexshader, mFragmentshader);

        // Validate opengL objects
        ShaderHelper.volidateProgram(program);

        // Use the program
        GLES20.glUseProgram(program);

    }


    /** * loads the attribute */ in the shader
    public void loadShaderAttributes(a) {
        // Get the shader attribute
        u_color = GLES20.glGetUniformLocation(program, "u_Color");
        a_position = GLES20.glGetAttribLocation(program, "a_Position");
        u_matrix = GLES20.glGetUniformLocation(program, "u_Matrix");
    }


    /** * bind the shader properties to the vertex data and enable the use of vertex */
    public void bindAttributes(a) {
        // Bind a_position and verticeData vertex positions
        / * * * the first parameter, the second parameter is the shader properties * and how much weight each vertex, we have only to the component * the third parameter, the fourth parameter data types * and is only meaningful plastic, ignore * 5 parameters, an array has multiple attributes to be meaningful, we only have an attribute, Pass 0 * sixth argument, where does OpengL read from */
        floatBuffer.position(0);
        GLES20.glVertexAttribPointer(a_position, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT,
                false.0, floatBuffer);
        // Start vertex
        GLES20.glEnableVertexAttribArray(a_position);
    }


    /** * Create an orthogonal matrix based on the width and height of the screen@paramWidth Screen width *@paramHeight Screen height */
    public void projectionMatrix(int width, int height) {

        // Create a perspective projection that starts at z-1 and ends at -10
        MatrixHelper.perspetiveM(mProjectionMatrix, 45, (float) width / (float) height, 1f.10f);

        // Create view matrix
        Matrix.setLookAtM(mViewMatrix, 0.0f.2f.2f.0f.0f.0f.0f.1f.0f);
    }


    /** * start drawing circles */
    public void draw(a) {
        // Set the circle's color to red
        // Set the matrix data
        Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

        positionTableInScreen();
        GLES20.glUniformMatrix4fv(u_matrix, 1.false, mViewModelProjectionMatrix, 0);

// Log.d("mmm", drawList.size() + "/");
        for(DrawCommand command : drawList) { command.draw(); }}private void positionTableInScreen(a) {
        // Matrix multiplication
        Matrix.multiplyMM(mViewModelProjectionMatrix, 0, mViewProjectionMatrix,
                0, mModelMatrix, 0);

    }

    public void translate(float x, float y, float z)// Set the move along the XYZ axis
    {
        Log.d("mmm"."Translation");
        Matrix.translateM(mModelMatrix, 0, x, y, z);
    }


    // Rotate transform
    public void rotate(float angle, float x, float y, float z) {// Set the move around the XYZ axis
        Log.d("mmm"."Rotation");
        Matrix.rotateM(mModelMatrix, 0, angle, x, y, z);
    }



    // Scale
    public  void scale(float x,float y,float z) {
        Matrix.scaleM(mModelMatrix, 0, x, y, z);
    }


        public interface DrawCommand {
        void draw(a); }}Copy the code
public class AirHockKeyRenderCircle implements GLSurfaceView.Renderer {
    private final Context mContext;
    private Circle circle;
    private Cylinder cylinder;

    / / draw circles


    public AirHockKeyRenderCircle(Context context) {
        this.mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GlsurfaceView calls this method when the surface is created. This happens in the application
        It is also called when you first run it or when you return from another Activity

        // Clear the screen
        GLES20.glClearColor(0.0 f.0.0 f.0.0 f.0.0 f);

// circle = new Circle();
// circle.init(mContext);

        cylinder = Cylinder.getInstance();
        cylinder.init(mContext);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // After the Surface is created, this method is called every time the Surface size changes, such as switching between horizontal and vertical screens
        // Set the screen size
        GLES20.glViewport(0.0, width, height);
        cylinder.projectionMatrix(width, height);
// circle.projectionMatrix(width,height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // When drawing each frame of data, this method is called. This method must draw something, even if it is just to empty the screen
        // Because when this method returns, the data in the render area will be exchanged and displayed on the screen. If nothing else, you will see a flicker effect

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        cylinder.draw();
// circle.draw();}}Copy the code
public class MainActivity extends AppCompatActivity {

    private GLSurfaceView glSurfaceView;
    private float mPreviousX;
    private float mPreviousY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        initData();
    }

    private void initData(a) {
        if (supportsEs2()) {
            AirHockKeyRenderCircle myGlRender = new AirHockKeyRenderCircle(this);
            // Set the OpengL version
            glSurfaceView.setEGLContextClientVersion(2);
            glSurfaceView.setRenderer(myGlRender);
            RENDERMODE_WHEN_DIRTY and RENDERMODE_CONTINUOUSLY. The former is lazy and needs to be called manually
            / / glSurfaceView. RequestRender () will be updated, while the latter is constantly rendering.
            glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
        } else {
            Log.d("mmm"."Version 2.0 is not supported"); }}private void initView(a) {
        glSurfaceView = findViewById(R.id.glsurface);
        glSurfaceView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float x = event.getX();// The current touch position X coordinates
                float y = event.getY();// The current touch position X coordinates

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
// Cylinder.getInstance().rotate(10, 1, 0, 0);
/ / Cylinder. The getInstance (). Scale (0.6 0.4 f, 1.5 f, f); // The xyz three directions are scaled according to their respective scaling factors

                        break;
                    case MotionEvent.ACTION_MOVE:// When a movement event is detected
                        float dx = x - mPreviousX;
                        float dy = y - mPreviousY;

                        if (dx > 0) {
                            Cylinder.getInstance().rotate(-dx, 1.0.0);
/ / Cylinder. The getInstance (). The translate (0.1 f, 0, 0);
                        } else  {
                            Cylinder.getInstance().rotate(dx, 1.0.0);
/ / Cylinder. The getInstance (). The translate (0.1 f, 0, 0);
                        }

                        if (dy > 0) {
                            Cylinder.getInstance().rotate(-dy, 0.0.1);
                        } else  {
                            Cylinder.getInstance().rotate(dy, 0.0.1);

                        }
                        break;
                }
                mPreviousX = x;
                mPreviousY = y;

                return true; }}); }private boolean supportsEs2(a) {
        // Check if the system supports OpenGL ES 2.0.
        ActivityManager activityManager =
                (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ConfigurationInfo configurationInfo = activityManager
                .getDeviceConfigurationInfo();
        // Even though the latest emulator supports OpenGL ES 2.0,
        // it has a bug where it doesn't set the reqGlEsVersion so
        // the above check doesn't work. The below will detect if the
        // app is running on an emulator, and assume that it supports
        / / OpenGL ES 2.0.
        final boolean supportsEs2 =
                configurationInfo.reqGlEsVersion >= 0x20000
                        || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
                        && (Build.FINGERPRINT.startsWith("generic")
                        || Build.FINGERPRINT.startsWith("unknown")
                        || Build.MODEL.contains("google_sdk")
                        || Build.MODEL.contains("Emulator")
                        || Build.MODEL.contains("Android SDK built for x86")));

        return supportsEs2;
    }

    @Override
    protected void onResume(a) {
        super.onResume();
        glSurfaceView.onResume();
    }

    @Override
    protected void onPause(a) {
        super.onPause(); glSurfaceView.onPause(); }}Copy the code