Android Fullscreen 全屏樣式

Android Fullscreen 全屏樣式
Android Fullscreen 全屏樣式

Android Studio設置全屏,通過編輯『themes.xml』

『themes.xml』
<style  name=”Theme.Fullscreen”  parent=”android:Theme.NoTitleBar.Fullscreen”>

編輯『AndroidManifest.xml』

『AndroidManifest.xml』
<application android:theme=”@style/Theme.Fullscreen”>

代碼係『eclipse』移稙過蒞, 『Android Studio』程式閃退. 因『MainActivity』繼承『AppCompatActivity』造成. 改為繼承『Activity』.

public class MainActivity extends  Activity

 

Android Studio NDK-OpenGL ES/EGL渲染

Android Studio NDK-OpenGL ES/EGL渲染
Android Studio NDK-OpenGL ES/EGL渲染

OpenGL EGL作為平臺冇関 API, 今程序冇視『Linux X Window』『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 布局OpenGL ES
  4. 先加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL實用架餐庫
#include <EGL/egl.h> EGL庫
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫ANativeWindow
  1. 冚辦爛EGL程式首先調用『eglGetDisplay()』, 得到『EGLDisplay』等同『原生視窗』, 『EGLDisplay』係整數索引.
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1. 索引大於零即成功, 因為等於零失敗.
EGL_NO_DISPLAY 0失敗
  1. 初始EGL程式, 幷獲得『major』主版號 , 『minor』副版號
EGLint major,minor;

eglInitialize(display,&major,&minor)  ;

  1. 失敗返回EGL_FALSE, 查錯誤碼eglGetError()
EGL_BAD_DISPLAY 非有效EGLDisplay
EGL_NOT_INITALIZED 未能初始
  1. 穩『surface』渲染配置
EGLBoolean eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
  1. EglChooseConfig()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLint *attrib_list 指定屬性列表, 以EGL_NONE結屘
EGLConfig *configs 返回配置列表
EGLint config_size 指定配置列表長度
EGLint *num_config 返回配置長度
  1. 『EGLConfig』屬性
屬性 描述
EGL_SURFACE_TYPE EGL渲染表面類型 EGL_WINDOW_BIT
EGL_RENDERABLE_TYPE OpenGL版本 EGL_OPENGL_ES_BIT
EGL_RED_SIZE 紅色量位 8Bit
EGL_GREEN_SIZE 綠色量位 8Bit
EGL_BLUE_SIZE 藍色量位 8Bit
EGL_ALPHA_SIZE 透明量位 8Bit
EGL_DEPTH_SIZE 深度量位 16Bit
EGL_NONE 屬性列表以EGL_NONE結屘 0

屬性列表

EGLint    attrib_array[32];
  1. 設定『EGLConfig』屬性
attrib_array[0] = EGL_SURFACE_TYPE; attrib_array[1] = EGL_WINDOW_BIT;
attrib_array[2] = EGL_RENDERABLE_TYPE; attrib_array[3] = EGL_OPENGL_ES_BIT;
attrib_array[4] = EGL_RED_SIZE; attrib_array[5] = 8;
attrib_array[6] = EGL_GREEN_SIZE; attrib_array[7] = 8,
attrib_array[8] = EGL_BLUE_SIZE; attrib_array[9] = 8;
attrib_array[10] = EGL_ALPHA_SIZE; attrib_array[11] = 8;
attrib_array[12] = EGL_DEPTH_SIZE; attrib_array[13] = 16;
attrib_array[14] = EGL_NONE;  
  1. 據 attrib_array 返回最隹配置config
EGLint    config_number = 32;

EGLConfig config_array[32];

EGLConfig  config;// 配置

eglChooseConfig(display,attrib_array,config_array,config_number,&config_number);

  1. 匹配最佳配置
for(int i=0;i<config_number;++i)    {

eglGetConfigAttrib(openGL->display, config_array[i],EGL_RED_SIZE, &red);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_GREEN_SIZE, &green);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_BLUE_SIZE, &blue);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_ALPHA_SIZE, &alpha);

eglGetConfigAttrib(openGL->display, config_array[i],EGL_DEPTH_SIZE, &depth);

if( red == 8 && green == 8 && blue == 8 && alpha == 8 && depth == 16){

openGL->config = config_array[i];// 最佳配置

break;

}

}

  1. 生成渲染視窗
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
  1. eglCreateWindowSurface()函式參數
參數 簡介
EGLDisplay dpy 『原生視窗』索引
EGLConfig config 最佳配置
EGLNativeWindowType win SurfaceView
const EGLint *attrib_list 屬性列表
  1. 渲染視窗屬性
視窗屬性 描述
EGL_RENDER_BUFFER 渲染緩存 EGL_SINGLE_BUFFER『單緩存』EGL_BACK_BUFFER『雙緩存』
EGL_NONE 屬性列表以EGL_NONE結屘 0
  1. 屬性
attrib_array[0] = EGL_RENDER_BUFFER; attrib_array[1] = EGL_BACK_BUFFER;
attrib_array[2] = EGL_NONE;  
  1. 生成渲染視窗
surface_draw = eglCreateWindowSurface(display,config,nativeWindow,attrib_array);
  1. 獲得錯誤碼
錯誤碼EGLint error = eglGetError(); 簡介
EGL_BAD_MATCH 視窗唔匹配EGLConfig唔支持渲染
EGL_BAD_CONFIG 視窗唔匹配EGLConfig系統晤支持
EGL_BAD_NATIVE_WINDOW 原生視窗句柄冇效
EGL_BAD_ALLOC 冇法分配資源
  1. 關聯描述表, 将『EGLContext』『EGLSurface』與當前線程關聯, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回
eglMakeCurrent(display,surface_draw,surface_draw,context);
  1. 『Native』層用『ANativeWindow 』蒞渲染.通過『Surface』得到『ANativeWindow』.
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface)
  1. Android 『java』層用『SurfaceView』蒞渲染, 提取『Surface』畀『Native』
SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surface_view);
SurfaceHolder     surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
Surface             surface = surfaceHolder.getSurface();

Android Studio NDK-OpenGL ES布局

Android Studio NDK-OpenGL ES布局
Android Studio NDK-OpenGL ES布局

『Android Studio NDK』提供『EGL』連接『OpenGL』, 『EGL』被設計出來,作爲 OpenGL 和原生窗口系統之間的橋梁『Microsoft Windows』『Mac OS X Quatz』差異.用統壹接口同原生視窗聯接. 跨平臺API更易於移值. 所以OpenGL比DirectX更得人鐘意.

  1. 使用NDK
  2. 編譯OpenGL ES
  3. 載入共享庫”app.so”,『app』系你庫名.
static {

System.loadLibrary(“app”);

}

  1. 首先修改布局『xml』添加『SurfaceView』,而非『GLSurfaceView』.
 <android.view.SurfaceView

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:id=”@+id/surface_view” />

  1. 係『xml』添加『全屏樣式』
<style  name=”FullscreenTheme”  parent=”android:Theme.NoTitleBar.Fullscreen”  >
  1. 係『java』加『android.view.SurfaceHolder.Callback』用蒞行『SurfaceView』『創建』『改變』『銷毀』
        @Override

public void surfaceCreated(SurfaceHolder surfaceHolder) {

new Thread(this).start();//渲染,啟動線程  Runnable.run()

}

        @Override

public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

}

        @Override

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

}

  1. 係『java』加『Runnable』用蒞行OpenGL 渲染線程, 『EGL』同『OpenGL』代碼需擺同壹綫程. 唔系eglSwapBuffers()返回EGL_BAD_SURFACE.
    @Override

public void run() {

init( );

while (true) {

update(0);

}

}

  1. 係『xml』為Google Play加版本過濾
<uses-sdk        android:minSdkVersion=”9″       android:targetSdkVersion=”19″ />
  1. 係『txt』穩庫文檔
find_library( OpenGL-lib libGLESv1_CM.so  )
find_library( OpenEGL-lib libEGL.so )
find_library( Android-lib libandroid.so )
  1. 係『txt』連連接庫文檔
target_link_libraries(app

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib})

  1. 加入頭文檔
#include <GLES/gl.h> 標準OpenGL頭文檔
#include <GLES/glext.h> OpenGL架餐庫
#include <EGL/egl.h> EGL頭文檔
#include <EGL/eglext.h> EGL架餐庫
#include <android/native_window_jni.h> 原生視窗庫

 

Android Studio NDK-OpenGL ES-3D相機gluPerspective()

Android Studio NDK-OpenGL ES-3D相機gluPerspective()
Android Studio NDK-OpenGL ES-3D相機gluPerspective()

將『OpenGL』移稙過『Andord』. 但OpenGL ES偏偏冇gluPerspective()視錐投影.但有glFrustumf()設定視口.

通過glFrustumf()設置視口, 得到 gluPerspective()視錐投影.
void gluPerspective(double  fovy,double aspect,double zNear,double zFar){

double  ymax = zNear * tan(fovy * 3.141592654f / 360.0f);

double  ymin = -ymax;

double    xmin = ymin * aspect;

double    xmax = ymax * aspect;

glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);

}

3D相機投影代碼

glViewport(0,0,viewport_width,viewport_height); 重置視區尺寸
glMatrixMode(GL_PROJECTION); 設定投影矩陣
glLoadIdentity(); 載入單位矩陣
gluPerspective(fov,aspect_ratio,near_clip_z,far_clip_z); 設置視錐體投影
glMatrixMode(GL_MODELVIEW); 設定模型視圖矩陣
glLoadIdentity(); 載入單位矩陣

 

Android Studio NDK-OpenGL-交換緩存畫面eglSwapBuffers()返回EGL_BAD_SURFACE

Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE
Android Studio NDK-OpenGL-交换緩存画面eglSwapBuffers()返回EGL_BAD_SURFACE

將『風水羅盤』由『Windows』移稙過『Andord』. 係交換緩存畫面『eglSwapBuffers()』返回『EGL_FALSE』, 『eglGetError()』返回『EGL_BAD_SURFACE』.

EGLBoolean ret = eglSwapBuffers(display,surface);

if(ret == EGL_FALSE){

GLint error = eglGetError();

if(error == EGL_BAD_SURFACE){

}

}

係『Windows』有『Winmain()』入口. 可以控制冚個整游戲運作, 係 『Winmain()』 游戲『運行』『渲染』都係主線程行.

而 『Andord』冇『Winmain()』. 游戲『運行』『渲染』要係新線程『Thread()』行. 而『EGLContext』同『EGLSurface』需係同壹線程『Thread()』生成, 唔係『eglSwapBuffers()』投『EGL_BAD_SURFACE』錯誤碼.

知錯係邊就易整, 將egl()程式擺係新線程『Thread()』行,而非『 onSurfaceCreated()』.

新線程『Thread()』
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);

 

 

Android Studio NDK- assets訪問讀

Android Studio NDK- assets讀係Android游戲唔將游戲資源擺係『res』而係『assets』, 事因『res』限制層次結構, 而『assets』允許任意檔案資料夾層次結構.位於『assets』檔案只讀唔寫. NDK需android 9版先支持.

係『CMakeLists.txt』添加『libandroid.so』庫

CMakeLists.txt
find_library( Android-lib libandroid.so )
target_link_libraries( geomanticcompass

${OpenGL-lib}

${OpenEGL-lib}

${Android-lib}

${log-lib})

係c檔案加入頭檔

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

『assets』檔通過『AssetManager』訪問

AssetManager assetManager = this.getAssets();

係『NDK』『assets』檔通過『AAssetManager』訪問, 『AAssetManager』接口

AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);

將『AssetManager』傳過去獲得『AAssetManager』

extern “C”

JNIEXPORT void JNICALL Java_net_bookcard_geomanticcompass_MainActivity_init(JNIEnv *env, jobject thiz, jobject egl_config, jobject asset_manager) {

AAssetManager *assetManager= AAssetManager_fromJava(env, asset_manager);

}

public native void init(AssetManager asset_manager);
init (getAssets());

 

開啟『assets』檔, 冇需畀絕對路徑, 而係畀『assets』相對路俓

AAsset *asset = AAssetManager_open(nativeasset, “name.bmp”, AASSET_MODE_BUFFER);

檔长度

int size = AAsset_getLength(asset)

分配記憶體

PBYTE data = (PBYTE)malloc(size);

讀『assets』檔

AAsset_read(asset, data, size);

載入分析

Load(data, size);

释放记忆体

free(data);

閂『assets』檔

AAsset_close(asset);

 

將游戲資源擺係『assets』下,『assets』『res』同位於『app\src\main』之下.

檔案資料夾 位置
assets D:\Android\game\app\src\main\assets
cpp D:\Android\game\app\src\main\cpp
java D:\Android\game\app\src\main\java
res D:\Android\game\app\src\main\res

 

 

Android Studio NDK-Frames幀

Android Studio NDK-Frames幀
Android Studio NDK-Frames幀

係游戲每『渲染』1『畫面』呌1『幀』Frame,  影書每秒24格『菲林』24『幀』, Android每秒至高60『幀』.

Android Studio NDK

  1. 通過clock()獲得進程時鐘 度量時間整數值.
  2. 通過sysconf(_SC_CLK_TCK) 獲得時鐘信號, 每秒時鐘單元值, 係Linux此值為100
  3. 計算秒 float seconds= (float)clock() / (float)sysconf(_SC_CLK_TCK);
#include <time.h>
clock_t clock(void)

 

#include <unistd.h>
long sysconf(int name)

 

量度程式耗時

long  clockTicks = sysconf(_SC_CLK_TCK);// 時鐘信號

clock_t startTime = clock();//當前時鈡

// 調用程式

clock_t currentTime = clock();//當前時鈡

float seconds = (float)(currentTime – startTime) / (float)clockTicks;

 

需要struct結構用蒞計『幀』Frame, 公式加下:

float _fps = Frames * (double)(currentTime – startTime) / (float)clockTicks ;

 

『幀』Frame
typedef struct FPS_TYP {

clock_t  startTime;// 啓動時鐘

long  clockTicks;// 時鐘信號

float Frames;// 每秒渲染幀數

int n;// 幀計

}FPS,*FPS_PTR;

 

計算每秒渲染幀
float Get_FPS(FPS_PTR fps){

++fps->n;// 幀加壹

if (fps->n > 100) {

fps->Frames = Get_FPS(fps, fps->n);

fps->n = 0;

}

return fps->Frames;

}

float Get_FPS(FPS_PTR fps, int  Frames){

clock_t  currentTime = clock();// 進程時鈡

double _fps = (double)Frames * (double)(currentTime – fps->startTime) / (float)fps->clockTicks ;

fps->startTime = currentTime;// 重置時間

return (float)_fps;

}

 

Android Studio NDK-編譯目標唔乾淨

Android Studio NDK-編譯目標唔乾淨
Android Studio NDK-編譯目標唔乾淨

Android Studio NDK編譯又蒞失敗, 『ninja: error: unknown target ‘clean’, did you mean ‘ninja -t clean’? 』大意目標唔乾淨.

  1. 撳『Win+E』進入NDK工程檔䅁夾.
  2. 進入『\app\.cxx\RelWithDebInfo\3t474c6q\』,數字檔䅁夾隨機生成
  3. 進入『arm64-v8a』『armeabi-v7a』『x86』『x86_64』檔䅁夾, 『冚辦爛』刪『子檔䅁』『子文檔』.
  4. 『arm64-v8a』『armeabi-v7a』『x86』『x86_64』保留唔刪
  5. 再蒞編譯掂
Build command failed.
Error while executing process C:\Users\bookc\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\ninja.exe with arguments {-C D:\Android\GeomanticCompass\app\.cxx\RelWithDebInfo\3t474c6q\arm64-v8a clean}

ninja: Entering directory `D:\Android\GeomanticCompass\app\.cxx\RelWithDebInfo\3t474c6q\arm64-v8a’

ninja: error: unknown target ‘clean’, did you mean ‘ninja -t clean’?

 

Android Studio NDK-OpenGL ES替換gluLookAt()

OpenGL ES替換gluLookAt()
OpenGL ES替換gluLookAt()

gluLookAt()  UVN相機模型,設定視點(相機)位置和視綫方向(矩陣運算).

void gluLookAt( GLdouble eyeX,

GLdouble eyeY,

GLdouble eyeZ,

GLdouble centerX,

GLdouble centerY,

GLdouble centerZ,

GLdouble upX,

GLdouble upY,

GLdouble upZ);

OpenGL ES冇gluLookAt() , 手工生成『旋轉矩陣』『相機位置』函式

void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,

GLfloat centerx, GLfloat centery, GLfloat centerz,

GLfloat upx, GLfloat upy, GLfloat upz)

{

GLfloat m[16];// 旋轉矩陣

GLfloat x[3], y[3], z[3];

GLfloat mag;

//生成旋轉矩陣

// Z矢量

z[0] = eyex – centerx;

z[1] = eyey – centery;

z[2] = eyez – centerz;

mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);

if (mag) {

z[0]/= mag;

z[1]/= mag;

z[2]/= mag;

}

 

// Y矢量

y[0] = upx;

y[1] = upy;

y[2] = upz;

 

// X 矢量 = Y 叉積 Z

x[0] = y[1] * z[2] – y[2] * z[1];

x[1] = -y[0] * z[2] + y[2] * z[0];

x[2] = y[0] * z[1] – y[1] * z[0];

 

// 重新計算 Y = Z 叉積 X

y[0] = z[1] * x[2] – z[2] * x[1];

y[1] = -z[0] * x[2] + z[2] * x[0];

y[2] = z[0] * x[1] – z[1] * x[0];

 

 

// 叉積給出了平行四邊形的面積,對于非垂直單位長度向量;所以在這裏標準化x,y

mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);

if (mag) {

x[0]/= mag;

x[1]/= mag;

x[2]/= mag;

}

 

mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);

if (mag) {

y[0]/= mag;

y[1]/= mag;

y[2]/= mag;

}

 

#define M(row,col)  m[col*4+row]

M(0, 0) = x[0];

M(0, 1) = x[1];

M(0, 2) = x[2];

M(0, 3) = 0.0;

M(1, 0) = y[0];

M(1, 1) = y[1];

M(1, 2) = y[2];

M(1, 3) = 0.0;

M(2, 0) = z[0];

M(2, 1) = z[1];

M(2, 2) = z[2];

M(2, 3) = 0.0;

M(3, 0) = 0.0;

M(3, 1) = 0.0;

M(3, 2) = 0.0;

M(3, 3) = 1.0;

#undef M

glMultMatrixf(m);

 

// 視點(相機)位置

glTranslatef(-eyex, -eyey, -eyez);

}

 

Android Studio NDK编译OpenGL ES

Android Studio NDK编译OpenGL ES
Android Studio NDK编译OpenGL ES

係Android Studio NDK编译OpenGL ES, 需鏈接『libGLESv1_CM.so』庫, 編輯『CMakeLists.txt』.

首先稳『libGLESv1_CM.so』庫

find_library( OpenGL-lib libGLESv1_CM.so )

鏈接『libGLESv1_CM.so』庫

target_link_libraries(  geomanticcompass

${OpenGL-lib}

${log-lib})