OpenGLES demo - 3. 建立Android工程

来源:互联网 发布:淘宝网评价管理在哪 编辑:程序博客网 时间:2024/06/07 03:31

原创文章,转载请注明连接 http://blog.csdn.net/hoytgm/article/details/32715655

Android主要都是Java代码,但是我们建立OpenGLES工程,主要还是调用C/C++代码,所以我们需要使用Jni(Java Native Interface)。这里代码的东西比较少,很多都是贴一些Java相关的,本人Java不太熟,所以很多地方就不解释了哈,要是有更好的代码,欢迎指正哈。

根目录下建立AndroidManifest.xml文件,内容照贴

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.hoytgm" >    <application android:icon="@drawable/icon" android:label="@string/app_name">        <activity android:name="hoytgm"                  android:label="@string/app_name"                  android:theme="@android:style/Theme.NoTitleBar.Fullscreen">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application><uses-sdk android:minSdkVersion="7" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /></manifest>


然后就是Android.mk,这个是Android下面的makefile,大家应该比我懂吧

## Build #LOCAL_PATH := $(call my-dir)# Build apkinclude $(CLEAR_VARS)LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin/LOCAL_PACKAGE_NAME := hoytgmLOCAL_SRC_FILES := \$(call all-subdir-java-files)LOCAL_JNI_SHARED_LIBRARIES := \libhoytgmLOCAL_MODULE_TAGS := eng optionalinclude $(BUILD_PACKAGE)# Build native shared objectinclude $(CLEAR_VARS)LOCAL_MODULE := libhoytgmLOCAL_SRC_FILES := \jni/jniapi.cpp \./main.cpp LOCAL_C_INCLUDES := \./LOCAL_SHARED_LIBRARIES := \liblog \libEGL \libGLESv2 \libandroid LOCAL_CFLAGS := \-Wall \-DANDROIDLOCAL_MODULE_TAGS := eng optionalLOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)

建立三个文件夹和相应的文件,参照我的层次哈,打括号是指目录

(jni) -> jniapi.cpp

(src) -> (com)--> (hoytgm) -> hoytgm.java

(res) -> (drawable)

(res) -> (layout) -> main.xml

(res) -> (values) -> strings.xml


然后是main.xml文件的内容

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    ><SurfaceView    android:id="@+id/surfaceview"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    /><TextView    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:textColor="#ffffffff"    android:textSize="60dp"    /></RelativeLayout>

和strings.xml的内容

<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">hoytgm</string></resources>


下面就差hoytgm.java和jniapi.cpp的内容了。这两个文件我们可以稍微讲一下吧。首先是hoytgm.java,需要improt的头
package com.hoytgm;import android.app.Activity;import android.os.Bundle;import android.widget.Toast;import android.view.Surface;import android.view.SurfaceView;import android.view.SurfaceHolder;import android.view.View;import android.view.View.OnClickListener;import android.util.Log;

然后实现一个SurfaceHolder的类,作为主要的activity

public class hoytgm extends Activity implements SurfaceHolder.Callback {}

在类里面添加一个成员用于打印标志

private static String TAG = "### hoytgm: ";


重载create函数

@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.i(TAG, "onCreate()");setContentView(R.layout.main);SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surfaceview);surfaceView.getHolder().addCallback(this);surfaceView.setOnClickListener(new OnClickListener() {public void onClick(View view) {}});}

重载几个状态的函数

@Overrideprotected void onStart() {super.onStart();Log.i(TAG, "onStart()");nativeOnStart();}@Overrideprotected void onResume() {super.onResume();Log.i(TAG, "onResume()");nativeOnResume();}@Overrideprotected void onPause() {super.onPause();Log.i(TAG, "onPause()");nativeOnPause();}@Overrideprotected void onStop() {super.onStop();Log.i(TAG, "onStop()");nativeOnStop();}public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {        Log.i(TAG, "surfaceChanged()");nativeSetSurface(holder.getSurface());}public void surfaceCreated(SurfaceHolder holder) {            Log.i(TAG, "surfaceCreated()");}public void surfaceDestroyed(SurfaceHolder holder) {nativeSetSurface(null);}

这里大家会发现一些native*()的函数,这些就是需要jniapi.cpp去实现的函数,这里把这些函数的声明也加到我们的这个类里面,并加载这个库

public static native void nativeOnStart();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("hoytgm");}

好了,到这里,我们的hoytgm.java就完成了,java相关的代码也结束了,可以开始写jniapi.cpp文件了,先一次性贴出整个文件的代码吧,因为这里就是按照之前的路径和jni的格式把hoytgm.java里面的几个native开头的函数实现了,然后自己创建一个线程用于做Opengl es相关的渲染工作。

/* */#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <math.h>#include <jni.h>#include <android/log.h>#include <android/native_window.h>#include <android/native_window_jni.h>#include <nativehelper/JNIHelp.h>#include <nativehelper/jni.h>#include <EGL/egl.h>#include <GLES2/gl2.h>#undef  LOG_TAG#define LOG_TAG "### hoytgm:"#define MAX_SIZE 1024#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)ANativeWindow* navWindow = 0;EGLDisplay eglDisplay = 0;EGLContext eglContext = 0;EGLSurface eglSurface = 0;pthread_t mainThreadId = 0;extern bool Render();extern void fini();extern bool init();enum{KEYCODE_BACK = 4};enum RenderMessage {    MSG_NONE = 0,    MSG_WINDOW_SET,    MSG_RENDER_LOOP_EXIT,};RenderMessage rendermsg;void destroy(){    LOG_I("destroy()");    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);    eglDestroyContext(eglDisplay, eglContext);    eglDestroySurface(eglDisplay, eglSurface);    eglTerminate(eglDisplay);    eglDisplay = EGL_NO_DISPLAY;    eglSurface = EGL_NO_SURFACE;    eglContext = EGL_NO_CONTEXT;    }void renderLoop() {    bool done = false;    LOG_I("renderLoop()");    static unsigned int framecount = 0;    while(!done)    {        if(framecount < 1)        {            LOG_I("no render occurs, sleep 500 ms");            usleep(500);        }        switch(rendermsg)        {        case MSG_WINDOW_SET:            init();            break;        case MSG_RENDER_LOOP_EXIT:            done = true;            fini();            destroy();            break;        default:            break;        }        rendermsg = MSG_NONE;        if(eglDisplay)        {            done = Render();            framecount++;            if(!eglSwapBuffers(eglDisplay, eglSurface))            {                if(framecount % 60 == 0)                {                    LOG_E("eglSwapBuffers() returned error: %d", eglGetError());                }            }        }    }}void* threadCallback(void* ){    renderLoop();    LOG_I("thread exit 0");    pthread_exit(0);    return 0;}extern "C" {JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnStart(JNIEnv* jenv, jobject obj){LOG_I("nativeOnStart()");    return;}JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnResume(JNIEnv* jenv, jobject obj){LOG_I("nativeOnResume()");    pthread_create(&mainThreadId, 0, threadCallback, 0);    LOG_I("thread id: %ld", mainThreadId);    return;}JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnPause(JNIEnv* jenv, jobject obj){LOG_I("nativeOnPause()");    rendermsg = MSG_RENDER_LOOP_EXIT;    return;}JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeOnStop(JNIEnv* jenv, jobject obj){LOG_I("nativeOnStop()");    rendermsg = MSG_RENDER_LOOP_EXIT;    return;}JNIEXPORT void JNICALL Java_com_hoytgm_hoytgm_nativeSetSurface(JNIEnv* jenv, jobject obj, jobject surface){    if(surface != 0)    {        navWindow = ANativeWindow_fromSurface(jenv, surface);        LOG_I("Got window %p", navWindow);        rendermsg = MSG_WINDOW_SET;    }    else    {        LOG_I("Releasing window");        ANativeWindow_release(navWindow);    }}} // extern "C"

到这里了,差不多就可以工作了吧?当然不行。。。不信你去根目录(有Android.mk)下编译,保证不过~~~因为我们还缺一个main.cpp,这个文件在Android.mk里面已经写好了, 但是我们没有创建并且实现它。而且,我们后面要做的所有工作将在这个文件里面完成,上面写的所有东西,以后几乎都不会碰了。根目录下创建main.cpp文件,这个里面部分渲染工作和第一章讲的都一样,唯一不同的是创建EGL。我觉得Andriod下面创建EGL的方法更有助于我们直接的去了解EGL的参数的创建过程,IOS上面一直觉得怪怪的。。。。。。

首先,我们需要定义一个数组来决定我们需要建立一个怎么样的EGL,也就是定义RGBA的bit大小,需不需要Depth,Stencil,要不要开MSAA等等。同时还需要定义一个额外的数组来告诉显卡我们需要哪个版本。

    const EGLint attribs[] =     {        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,        EGL_RED_SIZE,           8,        EGL_GREEN_SIZE,         8,        EGL_BLUE_SIZE,          8,        EGL_ALPHA_SIZE,         8,        EGL_DEPTH_SIZE,         0,        EGL_STENCIL_SIZE,       0,        EGL_SAMPLES,            0,        EGL_NONE,    };    EGLint attribListContext[] =    {        EGL_CONTEXT_CLIENT_VERSION, 2,        EGL_NONE    };


然后我们来得到display的句柄并初始化这个句柄
    egldisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);    if(egldisplay == EGL_NO_DISPLAY)    {        output_log("eglGetDisplay() returned error: 0x%x\n", eglGetError());        return false;    }    if(!eglInitialize(egldisplay, 0, 0))    {        output_log("eglInitialize() returned error: 0x%x\n", eglGetError());        return false;    }

我们需要知道当前的Display支持多少种配置,不出意外的话,我们刚才列出的那个属性就应该有对应的配置

    /* Get how many configs does the device support */    if(!eglGetConfigs(egldisplay, NULL, 0, &num_config))    {        output_log("eglGetConfigs(NULL) returned error: 0x%x\n", eglGetError());        return false;    }

好了,我们现在传入刚才的属性数组,看下能得到多少个满足这个条件的配置,也就是很重要的eglChooseConfig

    if(!eglChooseConfig(egldisplay, attribs, eglConfig_array, num_config, &supportConfigCount))    {        output_log("eglChooseConfig() returned error: 0x%x\n", eglGetError());        return false;    }

通过eglChooseConfig,我们就能得到我们支持的一个config id,用这个id,我们就能够创建一个eglcontext。eglcontext是一个非常重要的句柄,我们所需要画的任何东西,任何OpenGLES相关的操作,都需要在一个context下面才能工作。

    if(!(eglcontext = eglCreateContext(egldisplay, eglConfig, 0, attribListContext)))    {        output_log("eglCreateContext() returned error: 0x%x\n", eglGetError());        return false;    }

有了context,我们就差一个surface了,这个surface就相当于我们的backbuffer,所有画的操作和结果都是默认放在这个surface上面的

    if(!(eglsurface = eglCreateWindowSurface(egldisplay, eglConfig, navWindow, 0)))    {        output_log("eglCreateWindowSurface() returned error: 0x%x\n", eglGetError());        return false;    }

有了eglcontext和eglsurface了,我们再调用eglMakeCurrent将eglcontext设置为当前,我们就可以开始做GLES的工作了。

    if(!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext))    {        output_log("eglMakeCurrent() returned error: 0x%x\n", eglGetError());        return false;    }

到这里为止,我们在Android上面的工作基本就结束了。剩下的三个函数,init(), Render()和fini()都和IOS上面差不多了。有兴趣的同学可以去我的资源上面下载Android工程的源码并编译,会生成一个apk,就可以安装到虚拟机或者Android手机上面看结果。贴下代码的资源地址 http://download.csdn.net/detail/hoytgm/7532901

好了,Android工程的建立就到这里了。后面demo的代码主要都是在IOS上面实现的,用Android开发的同学们照着代码搬到Android应该没问题吧???调用都是相同的话,如果搬代码过程有什么问题,欢迎大家交流。

0 0
原创粉丝点击