1. 程式人生 > >Android OpenGL ES (二) 繪製三維/空間座標系

Android OpenGL ES (二) 繪製三維/空間座標系

 * 此檔案是關於3D座標軸的繪製,用jiasu.java和jiasu.xml實現了使用者介面
 * 關於3D處理的所有程式都在此檔案中
 *zjk 2014/03/04
 */
public class Jiasudu implements Renderer
{

float x=-0.5f,y=-0.5f,z=-0.5f;
private float r=0;
Handler handler,handler2;
private Timer timer = new Timer();
private TimerTask task;

// 定義Open GL ES繪製所需要的Buffer物件
FloatBuffer lineVerticesBuffer;
FloatBuffer xyzVerticesBuffer;
ByteBuffer lineFacetsBuffer;
ByteBuffer xiangliangFacetsBuffer;
ByteBuffer XFacetsBuffer;
ByteBuffer YFacetsBuffer;
ByteBuffer ZFacetsBuffer;

void updateXYZ(){                                                               //2.1建立各種陣列


// 定義立方體的8個頂點                                                       
float[] lineVertices = new float[] {
// 上頂面正方形的四個頂點
x, y, z,//0
x, 0,z,//1
0,0,z,//2
0,y,z,//3

// 下底面正方形的四個頂點
x,y,0,//4
x,0,0,//5
0,0,0,//6原點
0,y,0,//7 

};
//定義XYZ座標和顯示的字
float xyzVertices[]=new float[]{
-1.2f ,0f, 0f,//0 x起點,畫座標軸的

1.2f ,0f, 0f,//1 X軸的終點
1.0f,0.1f,0f,//2 X軸箭頭1
1.0f,-0.1f,0f,//3 X軸箭頭2

0f ,-1.2f , 0f,//4 Y軸起點
0f ,1.2f , 0f,//5 Y軸終點
0.1f ,1.0f ,0f,//6 Y軸箭頭1
-0.1f ,1.0f ,0f,//7 Y軸箭頭2

0f ,0f ,-1.2f,//8 Z軸起點
0f ,0f ,1.2f,//9 Z軸終點
0f ,0.1f ,1.0f,//10 Z軸箭頭1
0f ,-0.1f ,1.0f,//11 Z軸箭頭2

1.3f,0f,0f,//12 繪製字X
1.35f,0.1f,0f,//13
1.25f,0.1f,0f,//14
1.25f,-0.1f,0f,//15
1.35f,-0.1f,0f,//16

0f,1.4f,0f,//17 繪製字Y
0f,1.3f,0f,//18
0.05f,1.5f,0f,//19
-0.05f,1.5f,0f,//20

-0.05f ,0.05f ,1.25f,//21  繪製字Z
0.05f,0.05f,1.25f,//22
-0.05f,-0.05f,1.25f,//23
0.05f,-0.05f,1.25f,//24

//刻度X軸刻度
0.6f,0f,0f,//25
0.6f,0.1f,0f,//26
-0.6f,0f,0f,//27
-0.6f,0.1f,0f,//28
//刻度y軸刻度
0f,0.6f,0f,//29
-0.1f,0.6f,0f,//30
0f,-0.6f,0f,//31
-0.1f,-0.6f,0f,//32
//刻度Z軸刻度
0f,0f,0.6f,//33
0f,0.1f,0.6f,//34
0f,0f,-0.6f,//35
0f,0.1f,-0.6f//36

};
//畫長方體的12條邊     裡面表示的是從上面陣列中第幾個點到第幾個點畫直線
byte[] lineFacets = new byte[]{
0,1,
0,3,
0,4,
1,2,
1,5,
2,3,
2,6,
3,7,
4,5,
4,7,
5,6,
6,7
};
//向量從原點6指向長方體的0點  
byte[] xiangliangFacets = new byte[] {
6,0//6,0
};

//X座標及其箭頭  
byte[] XFacets = new byte[] {
//起終點
0,1,
//箭頭
1,2,
1,3,
//X
12,13,
12,14,
12,15,
12,16,
//X座標
25,26,
27,28

};
//Y座標及其箭頭  
byte[] YFacets = new byte[] {
//起終點
4,5,
//箭頭
5,6,
5,7,
//字Y
17,18,
17,19,
17,20,
//Y軸刻度
29,30,
31,32

};
//Z座標及其箭頭  
byte[] ZFacets = new byte[] {
//起終點
8,9,
//箭頭
9,10,
9,11,
//字Z
21,22,
22,23,
23,24,
//Z軸刻度
33,34,
35,36
};
// 將立方體的頂點位置資料陣列包裝成FloatBuffer;
lineVerticesBuffer = floatBufferUtil(lineVertices);
xyzVerticesBuffer = floatBufferUtil(xyzVertices);
// 將直線的陣列包裝成ByteBuffer
lineFacetsBuffer = ByteBuffer.wrap(lineFacets);
xiangliangFacetsBuffer = ByteBuffer.wrap(xiangliangFacets);
XFacetsBuffer = ByteBuffer.wrap(XFacets);
YFacetsBuffer = ByteBuffer.wrap(YFacets);
ZFacetsBuffer = ByteBuffer.wrap(ZFacets);
}
//建構函式帶了引數,是因為不同類間傳遞引數,將接受方的handler例項傳遞過來
@SuppressLint("HandlerLeak")
public Jiasudu(Handler handler_zjk) {

handler2=handler_zjk;//實際上handler2就是接收方的例項了,巧妙的轉換方法實現不同類間handler傳遞資料
handler = new Handler(){
public void handleMessage(Message msg) {
if(msg.what==200)//這是接收本類中定時器傳送過來的訊號用來更新正方體
updateXYZ();                                                              // 2.2

   }

};
//定時器任務中傳送了兩個訊號,給本類中傳送了一個,給activity類中發了一個
task = new TimerTask(){

  public void run() {
  String[] xyz = new String[3];//傳送給activity用的
//2s生成xyz的隨機數
x=(float) (Math.random()*(-2)+1);
y=(float) (Math.random()*(-2)+1);
z=(float) (Math.random()*(-2)+1);
//設定一下要顯示的XYZ位數,不管正負都顯示小數點後兩位
if(x>0)
xyz[0]=String.valueOf(x).substring(0, 4);
else 
xyz[0]=String.valueOf(x).substring(0, 5);
if(y>0)
xyz[1]=String.valueOf(y).substring(0, 4);
else 
xyz[1]=String.valueOf(y).substring(0, 5);
if(z>0)
xyz[2]=String.valueOf(z).substring(0, 4);
else 
xyz[2]=String.valueOf(z).substring(0, 5);
Message msg = new Message();
msg.what=200;//這是傳送給當前類中用來更新立方體的
handler.sendEmptyMessage(msg.what);

msg.what=0x123;//這是傳送給activity類中用來更新文字框中XYZ值得
Bundle bundle = new Bundle();//對資料包裝後用MSG傳送
//要傳送的是一個字串陣列,第一個引數是指定資料的名,當接收資料時可以用這個名來選擇獲取哪個資料,很方便,第二個引數是傳送的陣列
bundle.putStringArray("xyz", xyz);
msg.setData(bundle);
handler2.sendMessage(msg);//handler2指的是接收處的Handler例項,這裡因為不是同一類,所以要用建構函式將接受類的handler例項傳遞過來
  
      
  }
  };
  timer.schedule(task, 0, 7000);
}

        //2.3 實現接口裡的三個方法

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)                           //2.3.1
{
// 關閉抗抖動
gl.glDisable(GL10.GL_DITHER);
// 設定系統對透視進行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 0);
// 設定陰影平滑模式
gl.glShadeModel(GL10.GL_SMOOTH);
// 啟用深度測試
gl.glEnable(GL10.GL_DEPTH_TEST);
// 設定深度測試的型別
gl.glDepthFunc(GL10.GL_LEQUAL);

}


@Override
public void onSurfaceChanged(GL10 gl, int width, int height)                       //2.3.2
{
// 設定3D視窗的大小及位置
gl.glViewport(0, 0, width, height);
// 將當前矩陣模式設為投影矩陣
gl.glMatrixMode(GL10.GL_PROJECTION);
// 初始化單位矩陣
gl.glLoadIdentity();
// 計算透視視窗的寬度、高度比
float ratio = (float) width / height;
// 呼叫此方法設定透視視窗的空間大小。
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
}


// 繪製圖形的方法
@Override
public void onDrawFrame(GL10 gl)                 //2.3.3 
{
// 清除螢幕快取和深度快取
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 啟用頂點座標資料
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);                     //2.3.3.1
// 啟用頂點顏色資料
//gl.glEnableClientState(GL10.GL_COLOR_ARRAY);                   //2.3.3.2
// 設定當前矩陣模式為模型檢視。
gl.glMatrixMode(GL10.GL_MODELVIEW);

// --------------------繪製正方體---------------------
// 重置當前的模型檢視矩陣
gl.glLoadIdentity();
gl.glTranslatef(0.0f, -0.0f, -3.0f);//移動中心
// 沿著Y軸旋轉
gl.glRotatef(r, 0f, 0.1f, 0.0f);
r++;
// 沿著X軸旋轉
//gl.glRotatef(0f, 0.1f, 0f, 0f);
gl.glLineWidth(2.0f);
// 設定頂點的位置資料 因為所有的資料都在次陣列中,所以長方體和向量的只要設定這一次就好
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBuffer);                      //2.3.3.3
// 設定頂點的顏色資料
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);                       // 2.3.3.4
//gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 18);//這裡不用二維,用三維的畫法,注意是GL_LINES三維中畫線             
gl.glDrawElements(GL10.GL_LINES, lineFacetsBuffer.remaining(),               //2.3.3.5
GL10.GL_UNSIGNED_BYTE, lineFacetsBuffer);
// --------------------繪製向量---------------------
//繪製向量
gl.glLineWidth(6.0f);//直線寬度 5倍於其他線
//無需再設定點了,都是用的上面的陣列中的
//gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBuffer);//向量
gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);//向量
gl.glDrawElements(GL10.GL_LINES, xiangliangFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, xiangliangFacetsBuffer);//向量
// --------------------繪製X座標---------------------
//繪製x座標
gl.glLineWidth(3.0f);//直線寬度
//設定XYZ的頂點 因為所有XYZ的資料都在次陣列中,所以XYZ的只要設定這一次就好
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, xyzVerticesBuffer);
// 設定頂點的顏色資料
gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);//X
gl.glDrawElements(GL10.GL_LINES, XFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, XFacetsBuffer);//X
// --------------------繪製Y座標---------------------
//繪製Y座標
//無需再設定點了,都是用的上面的陣列中的
//gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBufferY);//Y
// 設定頂點的顏色資料
gl.glColor4f(1.0f, 1.0f, 0.0f, 1.0f);//Y
gl.glDrawElements(GL10.GL_LINES, YFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, YFacetsBuffer);//Y
// --------------------繪製Z座標---------------------
//繪製Z座標
//無需再設定點了,都是用的上面的陣列中的
//gl.glVertexPointer(3, GL10.GL_FLOAT, 0, lineVerticesBufferZ);//Y
// 設定頂點的顏色資料
gl.glColor4f(1.0f, 0.0f, 1.0f, 1.0f);//z
gl.glDrawElements(GL10.GL_LINES, ZFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, ZFacetsBuffer);//Z
// 繪製結束
gl.glFinish();                               //2.3.3.6
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// 旋轉角度增加1
//rotate+=1;
}
// 定義一個工具方法,將int[]陣列轉換為OpenGL ES所需的IntBuffer
//private IntBuffer intBufferUtil(int[] arr)
//{
//IntBuffer mBuffer;
//// 初始化ByteBuffer,長度為arr陣列的長度*4,因為一個int佔4個位元組
//ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
//// 陣列排列用nativeOrder
//qbb.order(ByteOrder.nativeOrder());
//mBuffer = qbb.asIntBuffer();
//mBuffer.put(arr);
//mBuffer.position(0);
//return mBuffer;
//}
// 定義一個工具方法,將float[]陣列轉換為OpenGL ES所需的FloatBuffer
private FloatBuffer floatBufferUtil(float[] arr)
{
FloatBuffer mBuffer;
// 初始化ByteBuffer,長度為arr陣列的長度*4,因為一個int佔4個位元組
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
// 陣列排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
mBuffer = qbb.asFloatBuffer();
mBuffer.put(arr);
mBuffer.position(0);
return mBuffer;
}