Andriod OpenGL 教程 10 - 3D世界
来源:互联网 发布:nba2k17球员数据 编辑:程序博客网 时间:2024/04/29 11:53
关键字: android OpenGL 移动开发 教程
到目前为止,我们已经学会了如何创建3D物体,给物体着色,在空间中打开灯光,给物体贴上纹理等基本功能。并有能力创建一个旋转的立方体或者一群闪烁的星星了,对3D编程也有了一定的了解。
这一课,我们将演示如何加载3D世界,并在3D世界中遨游。为此我们采用一个文本文件来定义我们的3D世界。这样可以方便地加载不同的3D世界定义文件,来得到不同的3D世界。
数据文件中每个三角形都以如下形式声明:
X1 Y1 Z1 U1 V1
X2 Y2 Z2 U2 V2
X3 Y3 Z3 U3 V3
保存在world .txt文件中。
类MyWorld方法loadWorld加载这个文件。
MyWorld.java
package wintop.gllesson10;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import java.util.ArrayList;import java.util.List;import java.util.StringTokenizer;import javax.microedition.khronos.opengles.GL10;import javax.microedition.khronos.opengles.GL11;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;import android.view.View.OnKeyListener;import android.view.View.OnTouchListener;import android.content.Context;import android.util.Log;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils;public class MyWorld implements OnKeyListener, OnTouchListener {// 纹理指针private int[] textures = new int[3];// 上下文句柄private Context context;// 3D 世界private Sector sector1;// 下面这些变量用于在3D世界中导航和头像的导航private final float piover180 = 0.0174532925f;private float heading;private float xpos;private float zpos;private float yrot; //Y 旋转private float walkbias = 0;private float walkbiasangle = 0;private float lookupdown = 0.0f;// 用于输入控制的变量和因子private float oldX; private float oldY;private final float TOUCH_SCALE = 0.2f;// // 顶点缓冲区private FloatBuffer vertexBuffer;// 纹理坐标缓冲区private FloatBuffer textureBuffer;// 最初的顶点定义private float[] vertices;// 纹理坐标(u, v)private float[] texture;// 构造函数public MyWorld(Context context) {this.context = context;}// 加载我们的3D世界// 参数fileName 从assets目录中加载的文件名public void loadWorld(String fileName) {try {// 一些临时变量int numtriangles = 0;int counter = 0;sector1 = new Sector();List<String> lines = null;StringTokenizer tokenizer;//用于输入文件的快速阅读器InputStream in_stream = this.context.getAssets().open(fileName);BufferedReader reader = new BufferedReader(new InputStreamReader(in_stream));//迭代读取所有的行String line = null;while((line = reader.readLine()) != null) {//跳过注释和空行if(line.startsWith("//") || line.trim().equals("")) {continue;}//读取这个文件包含多少个三角形if(line.startsWith("NUMPOLLIES")) {numtriangles = Integer.valueOf(line.split(" ")[1]);sector1.num_triangles = numtriangles;sector1.triangle = new Triangle[sector1.num_triangles];//读所有的其他行} else {if(lines == null) {lines = new ArrayList<String>();}lines.add(line);}}//清理reader.close();//现在迭代分析所有的行for(int loop = 0; loop < numtriangles; loop++) {//...定义三角形...Triangle triangle = new Triangle();//...然后yoga读取的五个点值赋给三角形的顶点 for(int vert = 0; vert < 3; vert++) {//line = lines.get(loop * 3 + vert);tokenizer = new StringTokenizer(line);//triangle.vertex[vert] = new Vertex();//triangle.vertex[vert].x = Float.valueOf(tokenizer.nextToken());triangle.vertex[vert].y = Float.valueOf(tokenizer.nextToken());triangle.vertex[vert].z = Float.valueOf(tokenizer.nextToken());triangle.vertex[vert].u = Float.valueOf(tokenizer.nextToken());triangle.vertex[vert].v = Float.valueOf(tokenizer.nextToken());}//最后,添加这些三角形到sectorsector1.triangle[counter++] = triangle;}//如果什么都没发生, 则写一个日志然后返回} catch(Exception e) {Log.e("World", "Could not load the World file!", e);return;}// 现在将顶点和纹理坐标分离出来vertices = new float[sector1.num_triangles * 3 * 3];texture = new float[sector1.num_triangles * 3 * 2];int vertCounter = 0;int texCounter = 0;for(Triangle triangle : sector1.triangle) {//for(Vertex vertex : triangle.vertex) {//vertices[vertCounter++] = vertex.x;vertices[vertCounter++] = vertex.y;vertices[vertCounter++] = vertex.z;//texture[texCounter++] = vertex.u;texture[texCounter++] = vertex.v;}}// 建立缓冲区ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);byteBuf.order(ByteOrder.nativeOrder());vertexBuffer = byteBuf.asFloatBuffer();vertexBuffer.put(vertices);vertexBuffer.position(0);byteBuf = ByteBuffer.allocateDirect(texture.length * 4);byteBuf.order(ByteOrder.nativeOrder());textureBuffer = byteBuf.asFloatBuffer();textureBuffer.put(texture);textureBuffer.position(0);}// 绘图public void draw(GL10 gl, int filter) {// 根据给定的滤波方式绑定纹理gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[filter]);float xtrans = -xpos;//用于游戏者沿X轴平移时的大小float ztrans = -zpos;//用于游戏者沿Z轴平移时的大小float ytrans = -walkbias - 0.25f;//用于头部的上下摆动float sceneroty = 360.0f - yrot;//位于游戏者方向的360度角//视图gl.glRotatef(lookupdown, 1.0f, 0, 0);//上下旋转gl.glRotatef(sceneroty, 0, 1.0f, 0);//根据游戏者正面所对方向所作的旋转gl.glTranslatef(xtrans, ytrans, ztrans);//以游戏者为中心的平移场景//使能顶点,纹理和法向状态gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);// 设置缓冲区数据指针gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);// 绘制所有的三角形gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertices.length / 3);// 返回前恢复原来的状态gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);}// 加载纹理public void loadGLTexture(GL10 gl, Context context) {// 从Android的资源目录获取纹理InputStream is = context.getResources().openRawResource(R.drawable.mud);Bitmap bitmap = null;try {//BitmapFactory 是 Android 图形库中处理图像的工具 bitmap = BitmapFactory.decodeStream(is);} finally {//总是要清理和关闭try {is.close();is = null;} catch (IOException e) {}}// 生成纹理指针gl.glGenTextures(3, textures, 0);// 生成Nearest方式的滤波纹理并绑定到纹理0gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);// 生成Linear方式的滤波纹理并绑定到纹理1gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[1]);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);// 生成mipmapped方式的滤波纹理并绑定到纹理2gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[2]);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);// 在Android系统SDK中,1.0版本没有buildMipMap类似的函数,因此这里// 检查OpenGL实例的版本,如果为1.1。则使用系统提供的方法,否在使用自定义的方法if(gl instanceof GL11) {gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);//} else {buildMipmap(gl, bitmap);}//清理bitmap.recycle();}// 建立自己的mipmap工具,总是按1/2的比例来缩小原图作为我们新的mipmap纹理层次。private void buildMipmap(GL10 gl, Bitmap bitmap) {//int level = 0;//int height = bitmap.getHeight();int width = bitmap.getWidth();//while(height >= 1 || width >= 1) {//首先, 根据位图生成纹理并设置器对应的层次GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);if(height == 1 || width == 1) {break;}//增加 mipmap 层次level++;height /= 2;width /= 2;Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);//清理bitmap.recycle();bitmap = bitmap2;}}///////// 用于3D世界的一些结构 //////////////// 一个包含纹理坐标的经典的Vertex类public class Vertex {public float x,y,z;public float u,v;}// Triangle类,包含三角形的所有顶点public class Triangle {public Vertex[] vertex = new Vertex[3];}// Sector类,保存我们的3D世界的所有三角形的个数和所有三角形public class Sector {public int num_triangles;public Triangle[] triangle;}// ===== 处理监听事件 ===== //// 重载键盘监听器以监听键盘事件@Overridepublic boolean onKey(View v, int keyCode, KeyEvent event) {// 处理键盘按下的事件if(event.getAction() == KeyEvent.ACTION_DOWN){return onKeyDown(keyCode, event);}return false;}/** * 检查 DPad 按下 左, 右, 上 和 下按钮. * 然后按相应的方向行走或者旋转头部. * * @参数 keyCode - 键值 * @参数 event - 键事件 * @返回值 事件处理了则返回true */public boolean onKeyDown(int keyCode, KeyEvent event) {if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {heading += 1.0f;yrot = heading;// 向左旋转场景} else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {heading -= 1.0f;yrot = heading;//向右侧旋转场景} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {xpos -= (float)Math.sin(heading * piover180) * 0.05f;//沿游戏者所在的X平面移动zpos -= (float)Math.cos(heading * piover180) * 0.05f;//沿游戏者所在的Z平面移动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;//使游戏者产生跳跃感} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {xpos += (float)Math.sin(heading * piover180) * 0.05f;//沿游戏者所在的X平面移动zpos += (float)Math.cos(heading * piover180) * 0.05f;//沿游戏者所在的Z平面移动if(walkbiasangle <= 1.0f) {//如果walkbiasangle小于1度walkbiasangle = 359.0f;//使 walkbiasangle 等于 359} else {walkbiasangle -= 10;//如果 walkbiasangle > 1 减去 10}walkbias = (float)Math.sin(walkbiasangle * piover180) / 20.0f;//使游戏者产生跳跃感}//事件处理完成后return true;}/** * 触屏方式处理. */@Overridepublic boolean onTouch(View v, MotionEvent event) {boolean handled = false;float x = event.getX(); float y = event.getY(); //如果在屏幕上移动 if(event.getAction() == MotionEvent.ACTION_MOVE) { //计算改变的值 float dx = x - oldX; float dy = y - oldY; //通过触摸上下移动 lookupdown += dy * TOUCH_SCALE; //通过触摸左右观看 heading += dx * TOUCH_SCALE; yrot = heading; //处理完事件后 handled = true; } //保存当前的值 oldX = x; oldY = y; return handled;}}
最终运行结果:
代码下载地址:http://download.csdn.net/detail/seniorwizard/4470542
- Andriod OpenGL 教程 10 - 3D世界
- Andriod OpenGL 教程 05 - 3D空间
- OpenGL教程之漫游3D世界
- 【Qt OpenGL教程】10:加载3D世界,并在其中漫游
- OpenGL 漫游3D世界
- Jeff Molofee(NeHe)的OpenGL教程--漫游3D世界
- android opengl es 3d世界
- NeHe OpenGL第十课:3D世界
- OpenGL基础图形编程 - OpenGL与3D图形世界
- OpenGL基础图形编程 - OpenGL与3D图形世界
- Andriod OpenGL 教程 04 - 旋转
- Andriod OpenGL 教程 08 - 混合
- Nehe教程第10课3D世界
- Win32 OpenGL编程(6) 踏入3D世界
- OpenGL教程之向3D进军
- 【Qt OpenGL教程】05:3D模型
- Andriod OpenGL 教程 02 - 第一个多边形
- Andriod OpenGL 教程 03 - 添加颜色
- ubuntu 10.04源列表
- 删除listview列表里面的单个item
- 32位保护模式下的寻址方式
- android文件存储的4种方式
- SAP MM 移动类型-入门篇
- Andriod OpenGL 教程 10 - 3D世界
- poj 1163 The Triangle 因为这个,发现DP很有意思
- 使用qemu进行内核源码级调试
- sqlserver查看数据库编码
- 关于文件的读写问题
- 不加new的初始化
- Logback日志系统配置攻略
- android 系统开发APK签名方法
- android 入门xml布局文件