1. Create a new Android Project with C/C++ support
In order to create a new Android project with C/C++ support, click here and follow the instructions.
The only difference is that you will use a different API level:
Minimum API level: API 21: Android 5.0 (Lollipop)
2. Add a new Activity
- Right click the package name
- Select New -> Kotlin File/Class.
- Fill in the following information:
- Name: MainActivity
- Kind: Class
- Click OK.
- Add the following code:
package dev.anastasioscho.glestriangle import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } }
- Open the AndroidManifest.xml file.
- Add the following in the application tag:
<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
At this point you can build and run your app.
3. Declare the OpenGL Requirement
We will use OpenGL ES 3.1 which adds the ability to do general purpose compute. That’s why we picked Android 5.0 (API Level 21) in the first place.
- Open the AndroidManifest.xml file.
- Add the following:
<uses-feature android:glEsVersion="0x00030001" android:required="true" />
4. Setup the GLSurfaceView
A GLSurfaceView is a specialised view where you can draw OpenGL ES graphics. The actual drawing is done in an GLSurfaceView.Renderer object.
- Right click the package name.
- Select New -> Kotlin File/Class.
- Fill in the following information:
- Name: CustomGLSurfaceView
- Kind: class
- Add the following code:
package dev.anastasioscho.glestriangle import android.content.Context import android.opengl.GLSurfaceView import javax.microedition.khronos.egl.EGLConfig import javax.microedition.khronos.opengles.GL10 class CustomGLSurfaceView(context: Context): GLSurfaceView(context) { private val customGLRenderer = CustomGLRenderer() init { setEGLContextClientVersion(3) setRenderer(customGLRenderer) } inner class CustomGLRenderer: Renderer { override fun onDrawFrame(gl: GL10?) {} override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {} override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {} } }
- Open the MainActivity.kt file.
- Make the following changes:
package dev.anastasioscho.glestriangle import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class MainActivity: AppCompatActivity() { private lateinit var customGLSurfaceView: GLSurfaceView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customGLSurfaceView = CustomGLSurfaceView(this) setContentView(customGLSurfaceView) } }
5. Create an XML Layout (Optional)
You can also use your CustomGLSurfaceView in an XML layout:
- Right click the res folder.
- Select New -> Android Resource File.
- Provide the following information:
- File name: activity_main
- Resource type: Layout
- Root element: LinearLayout
- Source set: main
- Directory name: layout
- Chosen qualifiers: Leave it empty
- Add the following code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <dev.anastasioscho.glestriangle.CustomGLSurfaceView android:id="@+id/customGLSurfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
- Open the MainActivity.kt file.
- Make the following changes:
package dev.anastasioscho.glestriangle import android.os.Bundle import androidx.appcompat.app.AppCompatActivity class MainActivity: AppCompatActivity() { private lateinit var customGLSurfaceView: GLSurfaceView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) customGLSurfaceView = findViewById(R.id.customGLSurfaceView) } }
- Open the CustomGLSurfaceView.kt file.
- Make the following changes:
package dev.anastasioscho.glestriangle import android.content.Context import android.opengl.GLSurfaceView import android.util.AttributeSet import javax.microedition.khronos.egl.EGLConfig import javax.microedition.khronos.opengles.GL10 class CustomGLSurfaceView(context: Context, attrs: AttributeSet): GLSurfaceView(context, attrs) { private val customGLRenderer = CustomGLRenderer() init { setEGLContextClientVersion(3) setRenderer(customGLRenderer) } inner class CustomGLRenderer: Renderer { override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {} override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {} override fun onDrawFrame(gl: GL10?) {} } }
6. Test the app
You will use OpenGL to paint the screen green.
6.1 Link the OpenGL library
- Open the CMakeLists.txt file.
- Add the following code:
cmake_minimum_required(VERSION 3.6.0) add_library(native-library SHARED main.cpp) find_library(opengl-lib GLESv3) target_link_libraries(native-library ${opengl-lib})
- Save the file and click Sync Now.
6.2 Create the NativeLibrary class
The purpose of this class is to load your native library and encapsulate the native functions.
- Right click the package name.
- Select New -> Kotlin File/Class.
- Provide the following information:
- Name: NativeLibrary
- Kind: Class
- Add the following code:
package dev.anastasioscho.glestriangle class NativeLibrary { companion object { init { System.loadLibrary("native-library") } } external fun nOnSurfaceCreated() external fun nOnSurfaceChanged(width: Int, height: Int) external fun nOnDrawFrame() }
6.3 Implement the native functions
- Open the main.cpp file.
- Add the following code:
#include <jni.h> #include <GLES3/gl31.h> extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnSurfaceCreated(JNIEnv * env, jobject obj) { glClearColor(0.0, 1.0, 0.0, 1.0); return; } extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnSurfaceChanged(JNIEnv * env, jobject obj, jint width, jint height) { glViewport(0, 0, width, height); return; } extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnDrawFrame(JNIEnv * env, jobject obj) { glClear(GL_COLOR_BUFFER_BIT); return; }
Warning
You must replace the dev_anastasios_glestriangle portion of the above functions with the appropriate components of your package name. So for example, if your package name is my.package.name then replace it with my_package_name.
6.4 Call the native functions
- Open the CustomGLSurface.kt file.
- Add the following code:
package dev.anastasioscho.glestriangle import android.content.Context import android.opengl.GLSurfaceView import android.util.AttributeSet import javax.microedition.khronos.egl.EGLConfig import javax.microedition.khronos.opengles.GL10 class CustomGLSurfaceView(context: Context, attrs: AttributeSet): GLSurfaceView(context, attrs) { private val customGLRenderer = CustomGLRenderer() private val nativeLibrary = NativeLibrary() init { setEGLContextClientVersion(3) setRenderer(customGLRenderer) } inner class CustomGLRenderer: Renderer { override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { nativeLibrary.nOnSurfaceCreated() } override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) { nativeLibrary.nOnSurfaceChanged(width, height) } override fun onDrawFrame(gl: GL10?) { nativeLibrary.nOnDrawFrame() } } }
Leave a Reply