最新版本:Android高级进阶十一 Android OpenGL建立3D空间
这一篇文章继续写在Android上使用OpenGL,前阶段的文章一直是旨在建立一个3D的形状,这一篇文章我们就来建立一个3D的空间模型。
老规矩先上效果图:
这次我们使用一个文件来存储我们所有顶点及纹理定点数据,具体使用方式待会儿见分晓。
代码如下:
WordActivity.java
Java代码
- package org.ourunix.android.world;
- import android.app.Activity;
- import android.opengl.GLSurfaceView;
- import android.os.Bundle;
- import android.view.KeyEvent;
- public class WordActivity extends Activity {
- /** Called when the activity is first created. */
- GLRenderer render;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- GLSurfaceView glView = new GLSurfaceView(this);
- render = new GLRenderer(this);
- glView.setRenderer(render);
- setContentView(glView);
- }
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- // TODO Auto-generated method stub
- return render.onKeyUp(keyCode, event);
- }
- }
ScData.java
Java代码
- package org.ourunix.android.world;
- import java.util.ArrayList;
- import java.util.List;
- public class ScData {
- public static class VERTEX{
- float x, y, z; //三角形的顶点坐标
- float u, v;//纹理坐标
- public VERTEX(float x, float y , float z, float u, float v){
- this.x = x;
- this.y = y;
- this.z = z;
- this.u = u;
- this.v = v;
- }
- }
- //三角形结构
- public static class TRIANGLE{
- VERTEX[] vertex = new VERTEX[3];
- }
- //区段结构
- public static class SECTOR{
- int numTriangles;
- List<TRIANGLE> triangle = new ArrayList<TRIANGLE>();
- }
- }
GLRenderer.java
Java代码
- package org.ourunix.android.world;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.nio.FloatBuffer;
- import java.nio.IntBuffer;
- import javax.microedition.khronos.egl.EGLConfig;
- import javax.microedition.khronos.opengles.GL10;
- import org.ourunix.android.world.ScData.SECTOR;
- import org.ourunix.android.world.ScData.TRIANGLE;
- import org.ourunix.android.world.ScData.VERTEX;
- import android.content.Context;
- import android.content.res.AssetManager;
- import android.content.res.Resources;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.opengl.GLUtils;
- import android.opengl.GLSurfaceView.Renderer;
- import android.view.KeyEvent;
- public class GLRenderer implements Renderer {
- private Context mContext;
- SECTOR sector1 = new SECTOR();
- public final static float piover180 = 0.0174532925f;
- float heading;
- float xpos;
- float zpos;
- float yrot; // Y Rotation
- float walkbias = 0;
- float walkbiasangle = 0;
- float lookupdown = 0.0f;
- float z=0.0f;
- private int[] texturesID = new int[3];
- public GLRenderer(Context ctx){
- mContext = ctx;
- }
- @Override
- public void onDrawFrame(GL10 gl) {
- // TODO Auto-generated method stub
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
- gl.glLoadIdentity(); // Reset The View
- float xtrans = -xpos;
- float ztrans = -zpos;
- float ytrans = -walkbias-0.25f;
- float sceneroty = 360.0f - yrot;
- FloatBuffer vertexPointer = Utils.BufferUtil.fBuffer(new float[9]);
- FloatBuffer texCoordPointer = Utils.BufferUtil.fBuffer(new float[6]);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexPointer);
- gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texCoordPointer);
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
- gl.glLoadIdentity();
- gl.glRotatef(lookupdown, 1.0f, 0.0f, 0.0f);
- gl.glRotatef(sceneroty, 0.0f, 1.0f, 0.0f);
- gl.glTranslatef(xtrans, ytrans, ztrans);
- gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesID[0]);
- for(TRIANGLE triangle : sector1.triangle)
- {
- vertexPointer.clear();
- texCoordPointer.clear();
- gl.glNormal3f(0.0f, 0.0f, 1.0f);
- for(int i=0; i<3; i++)
- {
- VERTEX vt = triangle.vertex[i];
- vertexPointer.put(vt.x);
- vertexPointer.put(vt.y);
- vertexPointer.put(vt.z);
- texCoordPointer.put(vt.u);
- texCoordPointer.put(vt.v);
- }
- gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 4);
- }
- gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
- gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
- }
- @Override
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- // TODO Auto-generated method stub
- float ratio = (float)width/height;
- gl.glViewport(0, 0, width, height);
- gl.glMatrixMode(GL10.GL_PROJECTION);
- gl.glLoadIdentity();
- gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
- gl.glMatrixMode(GL10.GL_MODELVIEW);
- gl.glLoadIdentity();
- }
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- // TODO Auto-generated method stub
- //加载纹理
- gl.glEnable(GL10.GL_TEXTURE_2D);
- LoadGLTextures(gl);
- //开启混色透明
- gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
- gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
- gl.glClearColor(0, 0, 0, 0);
- gl.glShadeModel(GL10.GL_SMOOTH);
- //开启深度测试
- gl.glClearDepthf(1.0f);
- gl.glDepthFunc(GL10.GL_LESS);
- gl.glEnable(GL10.GL_DEPTH_TEST);
- SetupWorld();
- }
- private void LoadGLTextures(GL10 gl) {
- int[] imgArray = {R.drawable.img};
- // TODO Auto-generated method stub
- for (int i = 0; i < 3; i++){
- IntBuffer textureBuffer = IntBuffer.allocate(1);
- gl.glGenTextures(1, textureBuffer);
- texturesID[i] = textureBuffer.get();
- gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesID[i]);
- Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), imgArray[0]);
- //生成纹理
- GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
- // 线形滤波
- gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
- gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
- }
- }
- private void SetupWorld() {
- // TODO Auto-generated method stub
- GLFile glFile = new GLFile(mContext.getResources());
- BufferedReader br = new BufferedReader(new InputStreamReader(glFile.getFile("data/world.txt")));
- TRIANGLE triangle = new TRIANGLE();
- int vertexIndex = 0;
- try {
- String line = null;
- while((line = br.readLine()) != null){
- if(line.trim().length() <= 0 || line.startsWith("/")){
- continue;
- }
- String part[] = line.trim().split("\\s+");
- float x = Float.valueOf(part[0]);
- float y = Float.valueOf(part[1]);
- float z = Float.valueOf(part[2]);
- float u = Float.valueOf(part[3]);
- float v = Float.valueOf(part[4]);
- VERTEX vertex = new VERTEX(x, y, z, u, v);
- triangle.vertex[vertexIndex] = vertex;
- vertexIndex ++;
- if(vertexIndex == 3){
- vertexIndex = 0;
- sector1.triangle.add(triangle);
- triangle = new TRIANGLE();
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public boolean onKeyUp(int keyCode, KeyEvent event)
- {
- switch ( keyCode )
- {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- yrot -= 1.5f;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- yrot += 1.5f;
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- // 沿游戏者所在的X平面移动
- xpos -= (float)Math.sin(heading*piover180) * 0.05f;
- // 沿游戏者所在的Z平面移动
- zpos -= (float)Math.cos(heading*piover180) * 0.05f;
- if (walkbiasangle >= 359.0f)// 如果walkbiasangle大于359度
- {
- walkbiasangle = 0.0f;// 将 walkbiasangle 设为0
- }
- else
- {
- walkbiasangle+= 10;// 如果 walkbiasangle < 359 ,则增加 10
- }
- // 使游戏者产生跳跃感
- walkbias = (float)Math.sin(walkbiasangle * piover180)/20.0f;
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- // 沿游戏者所在的X平面移动
- xpos += (float)Math.sin(heading*piover180) * 0.05f;
- // 沿游戏者所在的Z平面移动
- zpos += (float)Math.cos(heading*piover180) * 0.05f;
- // 如果walkbiasangle小于1度
- if (walkbiasangle <= 1.0f)
- {
- walkbiasangle = 359.0f;// 使 walkbiasangle 等于 359
- }
- else
- {
- walkbiasangle-= 10;// 如果 walkbiasangle > 1 减去 10
- }
- // 使游戏者产生跳跃感
- walkbias = (float)Math.sin(walkbiasangle * piover180)/20.0f;
- break;
- }
- return false;
- }
- public class GLFile
- {
- public Resources resources;
- public GLFile(Resources r)
- {
- resources = r;
- }
- public InputStream getFile(String name){
- AssetManager am = resources.getAssets();
- try {
- return am.open(name);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
- }
- }
Utils.java
Java代码
- package org.ourunix.android.world;
- import java.nio.ByteBuffer;
- import java.nio.ByteOrder;
- import java.nio.FloatBuffer;
- import java.nio.IntBuffer;
- import java.nio.ShortBuffer;
- public class Utils {
- public static class BufferUtil {
- public static IntBuffer intBuffer;
- public static FloatBuffer floatBuffer;
- public static ShortBuffer shortBuffer;
- public static IntBuffer iBuffer(int[] a) {
- // 先初始化buffer,数组的长度*4,因为一个float占4个字节
- ByteBuffer mbb = ByteBuffer.allocateDirect(a.length * 4);
- // 数组排列用nativeOrder
- mbb.order(ByteOrder.nativeOrder());
- intBuffer = mbb.asIntBuffer();
- intBuffer.put(a);
- intBuffer.position(0);
- return intBuffer;
- }
- public static FloatBuffer fBuffer(float[] a) {
- // 先初始化buffer,数组的长度*4,因为一个float占4个字节
- ByteBuffer mbb = ByteBuffer.allocateDirect(a.length * 4);
- // 数组排列用nativeOrder
- mbb.order(ByteOrder.nativeOrder());
- floatBuffer = mbb.asFloatBuffer();
- floatBuffer.put(a);
- floatBuffer.position(0);
- return floatBuffer;
- }
- public static ShortBuffer sBuffer(short[] a) {
- // 先初始化buffer,数组的长度*4,因为一个float占4个字节
- ByteBuffer mbb = ByteBuffer.allocateDirect(a.length * 2);
- // 数组排列用nativeOrder
- mbb.order(ByteOrder.nativeOrder());
- shortBuffer = mbb.asShortBuffer();
- shortBuffer.put(a);
- shortBuffer.position(0);
- return shortBuffer;
- }
- }
- }