3D graphics on Android projects based on native code

Report
Native OpenGL, OGRE3D on Android
Native code?
 What is native code?
 C/C++ code
 Using C/C++ API-s directly
 Why we need it, what should be moved into native
 In most situations native code should be avoided
 Performance critical parts
 Using external cross platform APIs
 Native calls have a cost!
Native code in an Android project
 Need separate SDK: Android NDK
 Cross compiling tools: ndk-build
 Must be familiar with Java Native Interface (JNI)
 Have to place native code in ./jni folder
 Need special files in ./jni:
 Android.mk
 Application.mk (optional)
 Have to call ndk-build before project build
 Should load the compiled library in Java code
 Use the library through native functions (JNI)
Java Native Interface I.
 Allows execution of native code from Java
 Native code is compiled into a dynamic library
 Library should be loaded with
System.loadLibrary(String libraryName)
 Library is accesed through native function calls
 Native functions does not have definition, they are
implemented in the native code:
package test.jni;
class A{
static{ System.loadLibrary(”myLibName”);}
protected native void myNativeFunc();
protected void myFunc(){myNativeFunc();}
}
Java Native Interface II.
 Native function declarations are special:
 Name expresses the package and class that declared it
 have special input parameters
 Can be generated with javah:
javah test.jni.A (A.class should be generated first)
 javah output:
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_test_jni_A_myNativeFunc (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
Java Native Interface III.
 Native code should be compiled into a library
 Compiled library should be accessible by the Java
application
 in case of Android it should be packed into the apk
Android native support
 javah can be used to generate native function
declarations
 ndk-build can be used to compile the native code to a
library file
 Ndk-build needs:
 Source files containing native function definitions
 They are typically located in ./jni
 Android.mk configuration file in ./jni
 This configuration file should be properly filled
 After ndk-build the project can be built as usual (with
NetBeans for e.g.)
Android.mk simple
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
Name of the library file to
be created
Source file in ./jni that
contains native code
definitions
LOCAL_MODULE := myLibName
LOCAL_SRC_FILES := mySourceFile.cpp
include $(BUILD_SHARED_LIBRARY)
Android.mk advanced
Additional used libraries
LOCAL_PATH := $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := OgreJNI
LOCAL_LDLIBS := -landroid -lc -lm -ldl -llog -lEGL -lGLESv2
LOCAL_LDLIBS += -L./../MyAPI/lib
Additional library path
LOCAL_LDLIBS += -lMyAPI
LOCAL_STATIC_LIBRARIES := cpufeatures
Additional include path
LOCAL_CFLAGS := -I./../MyAPI/include
LOCAL_CFLAGS += -fexceptions -frtti -x c++ -D___ANDROID___ DANDROID -DZZIP_OMIT_CONFIG_H
LOCAL_SRC_FILES := ./MyCode/MainActivity.cpp
include $(BUILD_SHARED_LIBRARY)
Source file not in ./jni,
Several source files can be
$(call import-module,android/cpufeatures)
listed
NetBeans/Eclipse native support
 NetBeans does not provide additional support for
native code in Android
 Eclipse
 Right click on project – Android Tools – Add native
support
 jni folder, Android.mk, empty cpp file created
automatically
 Android.mk refers to new cpp file
 Ndk-build called automatically before Java build
Pure Native Android Application I.
 At least we need a dummy Java Activity to call our
native functions
 We also have a built in „dummy” activity
 Calls „android_main” native function
 Separate thread
 Callbacks for window and input commands
 android.app.NativeActivity
 android_native_app_glue library
Pure Native Android Application II.
 Android .mk
LOCAL_STATIC_LIBRARIES := android_native_app_glue
…
include $(BUILD_SHARED_LIBRARY)
…
$(call import-module,android/native_app_glue)
 AndroidManifest.xml
 <uses-sdk android:minSdkVersion="9" />
 <activity android:name="android.app.NativeActivity"
Pure Native Android Application III.
 Source file example:
void android_main(struct android_app* state) {
state->onAppCmd = handle_cmd;
state->onInputEvent = handle_input;
while (1) {
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
while ((ident = ALooper_pollAll(-1, NULL, &events, (void**)&source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
}
…
}
}
if (state->destroyRequested) {
return;
}
Pure Native Android Application IV.
void handle_cmd(struct android_app* app, int32_t cmd) {
switch (cmd) {
case APP_CMD_INIT_WINDOW:
…
}
int32_t handle_input(struct android_app* app, AInputEvent* event) {
if(AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
float x = AMotionEvent_getX(event, 0);
…
return 1;
}
return 0;
}
OpenGL in native code I.
 Move performance critical
rendering parts to native code
 Keep the window and GUI on
the Java side
 Some features are easier to
access from Java
OpenGL in native code II.
 We need Anctivity with a Surface View:
public class NativeEglExample extends Activity implements SurfaceHolder.Callback
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
nativeOnCreate();
setContentView(R.layout.main);
SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surfaceview);
surfaceView.getHolder().addCallback(this);
}
protected void onResume() {
super.onResume();
nativeOnResume();
}
protected void onPause() {
super.onPause();
nativeOnPause();
}
...
protected void onStop() {
super.onStop();
nativeOnStop();
}
OpenGL in native code III.
…
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
nativeSetSurface(holder.getSurface());
}
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
nativeSetSurface(null);
}
public static native void nativeOnCreate();
public static native void nativeOnResume();
public static native void nativeOnPause();
public static native void nativeOnStop();
public static native void nativeSetSurface(Surface surface);
}
static {
System.loadLibrary("nativeegl");
}
OpenGL in native code IV.
 Native code:
void JNICALL …_nativeOnCreate(JNIEnv* jenv, jobject obj){
//do your initializations
}
void JNICALL …_nativeOnResume(JNIEnv* jenv, jobject obj){
//do your initializations
// you can start a main loop thread here that calls render()
}
OpenGL in native code V.
static ANativeWindow *window = 0;
JNIEXPORT void JNICALL …_nativeSetSurface(JNIEnv* jenv, jobject obj,
jobject surface)
{
if (surface == 0) {
ANativeWindow_release(window);
}
else {
window = ANativeWindow_fromSurface(jenv, surface);
initializeGLWindow();
}
return;
}
OpenGL in native code VI.
void initializeGLWindow()
{
const EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_NONE
};
EGLDisplay display;
EGLConfig config;
EGLint numConfigs;
EGLint format;
EGLSurface surface;
EGLContext context;
EGLint width;
EGLint height;
GLfloat ratio;
…
OpenGL in native code VII.
…
display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY);
eglInitialize(display, 0, 0);
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
surface = eglCreateWindowSurface(display, config, window, 0);
context = eglCreateContext(display, config, 0, 0);
eglMakeCurrent(display, surface, surface, context);
eglQuerySurface(display, surface, EGL_WIDTH, &width);
eglQuerySurface(display, surface, EGL_HEIGHT, &height);
glViewport(0, 0, width, height);
ratio = (GLfloat) width / height;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustumf(-ratio, ratio, -1, 1, 1, 10);
//other GL initialization
}
OpenGL in native code VIII.
void render()
{
//regular GL draw calls
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -3.0f);
//draw with arrays, no glBegin/glEnd in GLES
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glFrontFace(GL_CW);
glVertexPointer(3, GL_FIXED, 0, vertices);
glColorPointer(4, GL_FIXED, 0, colors);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE,
indices);
}
eglSwapBuffers(_display, _surface);
OpenGL in native code IX.
void JNICALL …_nativeOnStop(JNIEnv* jenv, jobject obj){
//do final cleanup
}
void JNICALL …_nativeOnPause(JNIEnv* jenv, jobject
obj){
//do your app state save if necessary
// you can end your main thread here
}
OpenGL in native code X. (Threads)
 Create a thread:
#include <pthread.h>
pthread_t _threadId;
pthread_create(&_threadId, 0, threadStartCallback, 0);
 Wait thread to terminate:
pthread_join(_threadId, 0);
 Render thread example:
void* threadStartCallback(void *arg)
{
while(running) //we can terminate this thread if needed
render();
pthread_exit(0);
return 0;
}
Pure native OpenGL app I.
 No Java code is written
 We use the native app glue
 android_main is called in its separate thread
 no additional threading needed on the native side
 GUI creation and accessing Android features is much
harder
Pure native OpenGL app II.
static ANativeWindow *window = 0;
void android_main(struct android_app* state) {
state->onAppCmd = handle_cmd;
state->onInputEvent = handle_input;
while (1) {
int ident, events;
struct android_poll_source* source;
while ((ident = ALooper_pollAll(-1, NULL, &events, (void**)&source)) >= 0) {
if (source != NULL) {
source->process(state, source);
}
if (state->destroyRequested) {
return;
}
}
render(); // same as before
}
}
void handle_cmd(struct android_app* app, int32_t cmd) {
switch (cmd) {
case APP_CMD_INIT_WINDOW:
window = state->window;
initializeGLWindow() ;//same as before
…
}
Using Ogre on Android





Now we can use native code in our Android app
Why not use Ogre3D?
It is possible …
Still under development (Ogre 1.9)
We have to compile Ogre for Android, instructions:
http://www.ogre3d.org/tikiwiki/CMake%20Quick%20Start%20Guide
?tikiversion=Android
 We should test if Ogre works on our device
 Run the compiled OgreSampleBrowser
 Also available on Google Play (before any build)
 In current state (Ogre 1.9 RC1) GLES2 render system is working
GLES1 is not
 It won’t run on emulator
 Min Android 2.3.3
Ogre3D with Java activity I.


Similar to native OpenGL with Java Activity
Initialization, window initialization and rendering is different
 Native code:
static Ogre::Root* gRoot = NULL;
void JNICALL …_nativeOnCreate(JNIEnv* jenv, jobject obj, jobject assetManager){
gRoot = new Ogre::Root();
gGLESPlugin = OGRE_NEW GLES2Plugin ();
gRoot->installPlugin(gGLESPlugin);
gOctreePlugin = OGRE_NEW OctreePlugin();
gRoot->installPlugin(gOctreePlugin);
//load additional plugins (particlefx, overlay)
gRoot->setRenderSystem(gRoot->getAvailableRenderers().at(0));
gRoot->initialise(false);
//enable loading media files from the apk asset folder
assetMgr = AAssetManager_fromJava(env, assetManager);
if (assetMgr)
{
ArchiveManager::getSingleton().addArchiveFactory( new APKFileSystemArchiveFactory(assetMgr)
);
ArchiveManager::getSingleton().addArchiveFactory( new APKZipArchiveFactory(assetMgr) );
}
//do your initializations
}
Ogre3D with Java activity II.
static ANativeWindow *window = 0;
static Ogre::RenderWindow* gRenderWnd = NULL;
JNIEXPORT void JNICALL …_nativeSetSurface(JNIEnv* jenv, jobject obj, jobject
surface)
{
if (surface == 0) {
ANativeWindow_release(window);
}
else {
window = ANativeWindow_fromSurface(jenv, surface);
initializeOgreWindow();
}
return;
}
Ogre3D with Java activity III.
void initializeOgreWindow()
{
//create render window based on an existing window
Ogre::NameValuePairList opt;
opt["externalWindowHandle"] = Ogre::StringConverter::toString((int)nativeWnd);
AConfiguration* config = AConfiguration_new();
AConfiguration_fromAssetManager(config, assetMgr);
opt["androidConfig"] = Ogre::StringConverter::toString((int)config);
gRenderWnd = Ogre::Root::getSingleton().createRenderWindow("OgreWindow", 0, 0, false, &opt);
ResourceGroupManager::getSingleton().addResourceLocation("/models", "APKFileSystem");
ResourceGroupManager::getSingleton().addResourceLocation("/material", "APKFileSystem");
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
//usual scene graph initialization
Ogre::SceneManager* pSceneMgr = gRoot->createSceneManager(Ogre::ST_GENERIC);
Ogre::Camera* pCamera = pSceneMgr->createCamera("MyCam");
pCamera->setPosition(100,100,500);
pCamera->lookAt(0,0,0);
Ogre::Viewport* vp = gRenderWnd->addViewport(pCamera);
vp->setBackgroundColour(Ogre::ColourValue(1,0,0));
…
}
Ogre3D with Java activity IV.
void render()
{
if(gRenderWnd != NULL && gRenderWnd->isActive())
{
try
{
gRenderWnd->windowMovedOrResized();
gRoot->renderOneFrame();
}
catch(Ogre::RenderingAPIException ex) {}
}
}
Ogre3D with Java activity V.

Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := OgreJNI
LOCAL_LDLIBS := -landroid -lc -lm -ldl -llog -lEGL -lGLESv2
LOCAL_LDLIBS += -L$(OGRE_ANDROID_PATH)/lib
LOCAL_LDLIBS += -L$(OGRE_ANDROID_PATH)/AndroidDependencies/lib/armeabi-v7a
LOCAL_LDLIBS += -lPlugin_OctreeSceneManagerStatic -lRenderSystem_GLES2Static -lOgreMainStatic
LOCAL_LDLIBS += -lzzip -lz -lFreeImage -lfreetype -lOIS
LOCAL_LDLIBS += -l$(OGRE_ANDROID_PATH)/systemlibs/armeabi-v7a/libsupc++.a
LOCAL_LDLIBS += -l$(OGRE_ANDROID_PATH)/systemlibs/armeabi-v7a/libstdc++.a
LOCAL_STATIC_LIBRARIES := cpufeatures
LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES=1
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)OgreMain/include
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)RenderSystems/GLES2/include
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)RenderSystems/GLES2/include/EGL
LOCAL_CFLAGS += -I$(ANDROID_NDK)/sources/cpufeatures
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)PlugIns/OctreeSceneManager/include
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)AndroidDependencies/include
LOCAL_CFLAGS += -I$(OGRE_ANDROID_PATH)AndroidDependencies/include/OIS
LOCAL_CFLAGS += -fexceptions -frtti -x c++ -D___ANDROID___ -DANDROID -DZZIP_OMIT_CONFIG_H
LOCAL_SRC_FILES := MainActivity.cpp
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/cpufeatures)
Using Ogre in pure native app I.
 Similar to pure native OpenGL application
 Code:
//globals
RenderWindow* gRenderWindow = NULL;
Root* gRoot = NULL;
static ANativeWindow *window = NULL;
Using Ogre in pure native app II.
void android_main(struct android_app* state) {
app_dummy();
gRoot = new Ogre::Root();
gRoot >installPlugin(OGRE_NEW GLES2Plugin());
gRoot >installPlugin(OGRE_NEW OctreePlugin());
gRoot >setRenderSystem(root->getAvailableRenderers().at(0));
gRoot >initialise(false);
ArchiveManager::getSingleton().addArchiveFactory( new APKFileSystemArchiveFactory(state->activity->assetManager) );
ArchiveManager::getSingleton().addArchiveFactory( new APKZipArchiveFactory(state->activity->assetManager) );
state->onAppCmd = handleCmd;
int ident, events;
struct android_poll_source* source;
while (true){
while ((ident = ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0){
if (source != NULL)
source->process(state, source);
if (state->destroyRequested != 0)
return;
}
if(renderWindow != NULL && renderWindow->isActive()){
gRenderWindow->windowMovedOrResized();
gRoot->renderOneFrame();
}
}
}
Using Ogre in pure native app III.
void handleCmd(struct android_app* app, int32_t cmd){
switch (cmd){
case APP_CMD_SAVE_STATE:
break;
case APP_CMD_INIT_WINDOW:
window = state->window;
initializeOgreWindow(); //same as above
break;
case APP_CMD_TERM_WINDOW:
if(gRoot && gRenderWindow)
static_cast<AndroidEGLWindow*>(gRenderWindow)>_destroyInternalResources();
break;
}
}

similar documents