Ophone平台2D游戏引擎实现——物理引擎(一)(二)
?http://dev.10086.cn/cmdn/wiki/index.php?doc-view-4271.html
http://dev.10086.cn/cmdn/wiki/index.php?doc-view-4273.html
免责声明:
本人纯属个人学习使用,并无商业行为。
本人对上述文章进行转载,进行部分的整理。如作者本人或其他版权单位有争议请通知本人,将立刻删帖,谢谢。
?
源码以打包在附件中:
?
(一)
上一篇文章我们介绍了常见的各种游戏特效的实现,你现在可以很轻松的实现各种游戏中所需要的特效,但是,你可能已经意识到了,我们的游戏一般都需要进行碰撞检测,比如前面的火柴棍小人,我们需要检测子弹和敌人之间的碰撞;碰撞检测通常是游戏开发的难点,作为引擎必然少不了碰撞检测部分,这里我们还是按照cocos2d的构架,使用Box2d作为物理引擎,下面我们将通过在OPhone平台实现一个小游戏,来对Box2d物理引擎进行学习。
Box2d
Box2D是一个用于游戏的2D刚体仿真库,它可以使物体的运动更加真实,让游戏场景看起来更具交互性。2D物理引擎能增强游戏世界中物体如多边形(砖块,三角形,多边形)的动作的真实感从而提高游戏质量。该引擎通过用户设定的参数如重力,密度,摩擦,弹性等参数计算碰撞,角度,力和动力等。这些计算需要大量的数学,物理等知识,如果有兴趣也可以下载其源码来研究。
Box2d同时也提供了各种语言环境的实现,由于Ophone平台使用Java作为变成语言,所以我们将选择使用Box2d的java版JBox2d,这也将产生一个问题,JBox2D是用processing库来处理图像显示,所以Ophone平台上则不适用,在Ophone平台上的图像渲染主要包括两种:Canvas和Opengl ES,因此我们可以任选其中一种,这里为了配合我们的引擎实现,选择通过Opengl ES来作为渲染部分,这部分就需要我们自己来实现,其实我们也可以不使用其图像渲染部分,因为我们主要是使用Box2d来做物理检测,稍后我们会通过一个实例游戏来介绍。
另外,比较优秀的2D物理引擎还有Chipmunk,对于谁好谁坏,我们这里不去评价,如果要使用Chipmunk作为物理引擎会比Box2d稍微苦难一些,因为Chipmunk目前没有Java版本,所以只能通过JNI方式来使用,这就需要使用NDK来开发原生的C程序,使用C语言来做,效率要高很多,但是开发,调试的难度也将增加,有机会我们将可以介绍如何使用NDK来编写C程序,并同时整合Chipmunk物理引擎。
这里只是我们对Box2d的一个简单介绍,让大家明白其用处,关于更多详细信息,大家可以参考其官方网站http://www.box2d.org/,图12-1则是cocs2d中演示的Box2d物理引擎效果,学完这部分内容,你也可以很轻松将其运行在Ophone平台上。
?
图12-1中这每个方块都具有重力,摩擦力,碰撞检测规则,他们都处于同一个世界场景中,不必眼红iPhone开发者,下面就给大家看一下,我们在Ophone平台提供的示例物理小游戏。
在学习使用Box2D引擎之前,我们需要了解一下一些常用的概念:
刚体(rigid body)
一块十分坚硬的物质,它上面的任何两点之间的距离都是完全不变的。它们就像钻石那样坚硬。我们用物体(body)来代替刚体。
形状(shape)
一块严格依附于物体(body)的 2D 碰撞几何结构(collision geometry)。形状具有摩擦(friction)和恢复(restitution)的材料性质。
约束(constraint)
一个约束(constraint)就是消除物体自由度的物理连接。在 2D 中,一个物体有 3 个自由度。如果我们把一个物体钉在墙上(像摆锤那样),那我们就把它约束到了墙上。这样,此物体就只能绕着这个钉子旋转,所以这个约束消除了它 2 个自由度。
接触约束(contact constraint)
一个防止刚体穿透,以及用于模拟摩擦(friction)和恢复(restitution)的特殊约束。你永远都不必创建一个接触约束,它们会自动被 Box2D 创建。
关节(joint)
它是一种用于把两个或多个物体固定到一起的约束。Box2D 支持的关节类型有:旋转,棱柱,距离等等。关节可以支持限制(limits)和马达(motors)。
关节限制(joint limit)
一个关节限制(joint limit)限定了一个关节的运动范围。例如人类的胳膊肘只能做某一范围角度的运动。
关节马达(joint motor)
一个关节马达能依照关节的自由度来驱动所连接的物体。例如,你可以使用一个马达来驱动一个肘的旋转。
世界(world)
一个物理世界就是物体,形状和约束相互作用的集合。Box2D 支持创建多个世界,但这通常是不必要的。
这里先给大家介绍就是让大家明白Box2d包括哪些内容,稍后对框架的介绍时就能更加容易理解,当然对于这些具体的功能,我们会在后面跟着示例代码一起学习。
Ophone Box2d
首先分析一下我们在Ophone平台上的Box2dDemo需要实现什么功能,首先我们将整个屏幕构建成一个盒子,然后再盒子中设置各种障碍,当我们触摸屏幕上任意位置时,就释放一个当前选择的物体,然后该物体将受到重力等因素的影响开始运动,直到最后静止下来。运行效果如图12-2所示。
?
屏幕中间的长条则是我们设置的障碍,而圆形和矩形都是我们在点击屏幕时要释放的物体,前面我们说过,JBox2d中的图形部分在Ophone中不能用,所以我们会专门介绍如何通过Opengl ES来对图形图像进行渲染,另外,该示例中的这些物体都是通过纹理映射来将图片映射到四边形上。为了大家能掌握图形系统相关内容,我们还实现了一个功能,玩家可以自己设置障碍,只需要点击Menu中,选择"编辑模式"就可以进入障碍编辑状态,如图12-3所似。
?
当障碍编辑完成之后再次选择编辑模式,则恢复到游戏中,此时我们所编辑的这些障碍都会正常的运行。当然该过程我们并没有使用引擎来完成,目的在于让大家更清楚渲染的原理,以及代码能够更多的重用,也就是说,大家可以直接拷贝代码到需要的游戏中去即可。同时大家也能掌握很多Opengl ES的相关知识。
Ophone平台如何使用JBox2d
要使用JBox2d我们首先需要获得其源码或者jar包,这个就不用多说了,知道其官方网站下载即可,这里我们下载了一个完整版本jbox2d-2.0.1-full.jar,让后将其放入我们所建立OphoneBox2d工程的lib文件夹下,JBox2d中大致包含了如图12-4所示的一些包:
?
图12-4 JBox2d结构图
?
其中org.jbox2d.collision比较重要,主要负责处理碰撞相关,包括对一些多边形的实现,这里所说的多边形主要是一些数据,比如多边形的位置,大小,重力,形状,质量等属性;org.jbox2d.common包主要用来设置一些全局的属性(Setting.java),调试时所使用的颜色(Color3f.java),以及其他的一些数学相关的内容,因为我们说了Box2d他主要不是来做渲染的,但是有时候我们需要知道所设置的这些物体是否正确,进行调试,就需要绘制这些简单的图形,并显示出来,供我们调试;org.jbox2d.dynamics包主要负责动力学相关的内容,下面是常见的功能包描述。
org.jbox2d.collision包
AABB:AABB坐标
OBB:OBB坐标
ContactID:接触ID
ContactPoint:接触点
ManifoldPoint:繁殖点
Segment:线段
Shape:外形基类
ShapeDef:外形定义基类
CircleDef:圆外形定义
CircleShape:圆外形
FilterData:碰撞过滤器
MassData:质量运算器
PolygonDef:多边开定义
PolygonShape:凸多边形
org.jbox2d.common包
Color3f:调试绘图颜色
Settings:全局设置
Mat22:2*2 矩阵
Sweep:碰撞描述
Vec2:向量(x ,y)
XForm:坐标转换,平移或旋转
标准的版本中还会存在Mat33表示3*3的矩阵和Vec3向量(x,y,z),该java版本中没有出现这些。
org.jbox2d.dynamics包
Body:刚体或叫物体
BodyDef:刚体定义
BoundaryListener:世界边界侦听
ContactFilter:继承这个类用来获取过滤碰撞
ContactListener:继承这个类用来获取碰撞结果
DebugDraw:调试绘图,用于调试
DestructionListener:关节或外形销毁时处理方法
World:物理世界
org.jbox2d.dynamics.contacts
Contact:管理两个外形接触
ContactEdge:接触边用来连接多个物体和接触到一个接触表
ContactResult:记录接触结果
org.jbox2d.dynamics.Joints
DistanceJoint:距离校正器
DistanceJointDef:距离连接定义
GearJoint:齿轮
GearJointDef:齿轮连接定义
Joint:连接基类
JointDef:连接定义基类
JointEdge:用于组合刚体或连接到一起.刚体相当于节点,而连接相当于边
MouseJoint:鼠标连接
MouseJointDef:鼠标连接定义
PrismaticJoint:棱柱连接
PrismaticJointDef:棱柱连接定义
PulleyJoint:滑轮连接
PulleyJointDef:滑轮连接定义
RevoluteJoint:旋转连接
RevoluteJointDef:旋转连接定义
org.jbox2d.testbed:主要是一些用来测试的程序
添加JBox2d到Ophone项目中
要在工程中使用JBox2d库,需要将JBox2d添加到工程中,添加方法如下:
右键单击工程,选择"Properties",进入项目Properties界面。
选择"Java Build Path",选择"Libraries"选项卡。
在点击"Add Jars..."按钮,添加Jar。
选择当前工程中我们之前放入lib文件夹中的jbox2d-2.0.1-full.jar文件,如图12-5所示,单击"确定"按钮即可。
?
OphoneBox2d框架
现在工程的结构展开应该如图12-6所示。
?
其中实现该工程的文件如下:
Box2dTest:工程Activity,入口
GameGLSurfaceView:游戏GLSurfaceView
GLRenderer:Opengl es渲染器
DrawObject:使用Opengl ES来绘制常用图形(矩形,圆形)
PhysicsWorld:物理世界场景
OphoneBox2d实现
开始分析代码之前,我们先确定一下需要准备的资源图片,从图12-2所示,我们可以看出,多少需要一个矩形和一个圆形的图片(当然也可直接指定颜色绘制矩形和圆形),这里我们将使用图片来进行纹理映射,该工程所需要的纹理图片如图12-7所示。
?
Box2dTest实现
该类继承自Activity,将作为本程序的入口,授予我们是通过Opengl ES来渲染的,所以构建需要构建一个GLSurfaceView对象作为Opengl ES的窗口,然后通过setContentView函数来设置显示该窗口视图。然后分别在onPause和onResume函数中调用GLSurfaceView类的GLSurfaceView。当然这也是所有Opengl ES程序的渲染基础框架,所有的Opengl ES程序窗口都由GLSurfaceView来实现。具体实现入代码清单12-1所示。
代码清单12-1:Box2dTest.java
?
view plaincopy to clipboardprint?
01.import android.app.Activity;??
02.import android.content.pm.ActivityInfo;??
03.import android.opengl.GLSurfaceView;??
04.import android.os.Bundle;??
05.import android.view.Menu;??
06.import android.view.MenuItem;??
07.import android.view.WindowManager;??
08.?
09.public class Box2dTest extends Activity {??
10.??? private GLSurfaceView mGLView;??
11.?
12.??? /** Called when the activity is first created. */?
13.??? @Override?
14.??? public void onCreate(Bundle savedInstanceState) {??
15.??????? super.onCreate(savedInstanceState);??
16.??????? // 构建GLSurfaceView视图??
17.??????? mGLView = new GameGLSurfaceView(this);??
18.??????? setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);??
19.??????? getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);??
20.??????? // 设置GLSurfaceView视图??
21.??????? setContentView(mGLView);??
22.??? }??
23.?
24.??? protected void onPause() {??
25.??????? super.onPause();??
26.??????? mGLView.onPause();??
27.??? }??
28.?
29.??? protected void onResume() {??
30.??????? super.onResume();??
31.??????? mGLView.onResume();??
32.??? }??
33.?
34.??? // 创建按钮??
35.??? public boolean onCreateOptionsMenu(Menu menu) {??
36.??????? menu.add(Menu.NONE, 1, Menu.NONE, "编辑模式");??
37.??????? menu.add(Menu.NONE, 2, Menu.NONE, "选择模型");??
38.??????? return true;??
39.??? }??
40.?
41.??? // 按钮事件处理??
42.??? public boolean onOptionsItemSelected(MenuItem item) {??
43.??????? switch (item.getItemId()) {??
44.??????? case 1:??
45.??????????? // 切换编辑模式??
46.??????????? ((GameGLSurfaceView) mGLView).toggleEdit();??
47.??????????? return true;??
48.??????? case 2:??
49.??????????? // 选择模型??
50.??????????? ((GameGLSurfaceView) mGLView).toggleModel();??
51.??????????? return true;??
52.??????? }??
53.??????? return false;??
54.??? }??
55.?
56.}?
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
public class Box2dTest extends Activity {
?private GLSurfaceView mGLView;
?/** Called when the activity is first created. */
?@Override
?public void onCreate(Bundle savedInstanceState) {
??super.onCreate(savedInstanceState);
??// 构建GLSurfaceView视图
??mGLView = new GameGLSurfaceView(this);
??setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
??getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
??// 设置GLSurfaceView视图
??setContentView(mGLView);
?}
?protected void onPause() {
??super.onPause();
??mGLView.onPause();
?}
?protected void onResume() {
??super.onResume();
??mGLView.onResume();
?}
?// 创建按钮
?public boolean onCreateOptionsMenu(Menu menu) {
??menu.add(Menu.NONE, 1, Menu.NONE, "编辑模式");
??menu.add(Menu.NONE, 2, Menu.NONE, "选择模型");
??return true;
?}
?// 按钮事件处理
?public boolean onOptionsItemSelected(MenuItem item) {
??switch (item.getItemId()) {
??case 1:
???// 切换编辑模式
???((GameGLSurfaceView) mGLView).toggleEdit();
???return true;
??case 2:
???// 选择模型
???((GameGLSurfaceView) mGLView).toggleModel();
???return true;
??}
??return false;
?}
}
?前面我们说了,可以通过Menu来切换模式和选择模型,因此我们在onCreateOptionsMenu中添加了两个菜单选项,其中"编辑模式"用来切换编辑状态和游戏状态,"选择模型"则用于选择我们触摸屏幕时所释放的模型,这里主要包括矩形和圆形,因此在菜单事件处理函数onOptionsItemSelected中,我们分别对两个菜单选项进行了处理,具体实现位于GameGLSurfaceView中。
GameGLSurfaceView实现
要实现一个用于显示Opengl ES窗口程序的视图,Ophone为我们提供了GLSurfaceView类,因此我们的GameGLSurfaceView类可以继承自GLSurfaceView很轻松的实现Opengl ES视图,该视图中有一个重要的部分,那就是Renderer,每一个Opengl ES窗口视图都需要一个渲染器来负责渲染。本例中的渲染器GLRenderer直接通过继承自Renderer来实现,GLSurfaceView中可以通过setRenderer来设置一个自定义的渲染器。具体实现入代码清单12-2所示。
代码清单12-2:GLSurfaceView.java
view plaincopy to clipboardprint?
01.package com.oger.demo.box2d.activity;??
02.?
03.import android.content.Context;??
04.import android.opengl.GLSurfaceView;??
05.import android.view.MotionEvent;??
06.?
07.public class GameGLSurfaceView extends GLSurfaceView {??
08.??? GLRenderer mRenderer;??
09.?
10.??? public GameGLSurfaceView(Context context) {??
11.??????? super(context);??
12.??????? mRenderer = new GLRenderer(context);??
13.??????? setRenderer(mRenderer);??
14.??? }??
15.?
16.??? public void toggleEdit() {??
17.??????? mRenderer.toggleEdit();??
18.??? }??
19.?
20.??? public void toggleModel() {??
21.??????? mRenderer.switchModel();??
22.??? }??
23.?
24.??? public boolean onTouchEvent(final MotionEvent event) {??
25.??????? mRenderer.setSize(this.getWidth(), this.getHeight());??
26.??????? // 线程通信??
27.??????? queueEvent(new Runnable() {??
28.??????????? public void run() {??
29.??????????????? mRenderer.touchEvent(event.getX(), event.getY(),??
30.??????????????????????? event.getAction());??
31.??????????? }??
32.??????? });??
33.??????? // sleep??
34.??????? try {??
35.??????????? Thread.sleep(20);??
36.??????? } catch (InterruptedException e) {??
37.??????????? e.printStackTrace();??
38.??????? }??
39.??????? return true;??
40.??? }??
41.}?
package com.oger.demo.box2d.activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
public class GameGLSurfaceView extends GLSurfaceView {
?GLRenderer mRenderer;
?public GameGLSurfaceView(Context context) {
??super(context);
??mRenderer = new GLRenderer(context);
??setRenderer(mRenderer);
?}
?public void toggleEdit() {
??mRenderer.toggleEdit();
?}
?public void toggleModel() {
??mRenderer.switchModel();
?}
?public boolean onTouchEvent(final MotionEvent event) {
??mRenderer.setSize(this.getWidth(), this.getHeight());
??// 线程通信
??queueEvent(new Runnable() {
???public void run() {
????mRenderer.touchEvent(event.getX(), event.getY(),
??????event.getAction());
???}
??});
??// sleep
??try {
???Thread.sleep(20);
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
??return true;
?}
}??
?总结
本文主要介绍了开源2D物理引擎Box2d的一些功能特征,以及如何在Ophone平台上使用Box2d的java版JBox2d;同时我们完成了使用该物理引擎实现的小游戏的框架,接下来我们就将通过完整实现该游戏的过程来学习使用JBox2d作为游戏开发中的物理引擎部分,同时大家可以考虑如何使用JNI来使用Box2d开发原生程序,加油哦,下一篇文章我们就将做出一个物理效果很酷的游戏来了。由于分享经验的心情急切,难免会出现一些疏忽或错误,还望不吝赐教!
?
(二)
?
接着上一篇我们构建的游戏框架继续完善,加上渲染器(GLRenderer),加上物理系统(PhysicsWorld),使用Opengl ES来作为JBox2d的图像管然系统—rawObject),就将完成上一篇中给大家演示的示例游戏了,另外,这一节我们还将介绍一些Opengl ES相关的知识,如果你不太熟悉就可以google一些相关资料,同时你还需要熟悉一些基本的物理学概念,例如质量,力,扭矩和冲量。因为它可以使你很好地了解一些基本概念,以便你使用 Box2D,同时如果你好奇 Box2D 内部是如何工作的,你可以看这些文档。
渲染器(GLRenderer)
上一篇我们完成了GameGLSurfaceView,同时也将其渲染器设置为了GLRenderer,它才是我们所有Opengl ES程序的核心,GLRenderer将继承自GLSurfaceView.Renderer,需要实现以下三个接口:
onSurfaceCreated():该方法在渲染开始前调用,OpenGL ES的绘制上下文被重建时也会被调用。当activity暂停时绘制上下文会丢失,当activity继续时,绘制上下文会被重建。另外,创建长期存在的OpenGL资源(如texture)往往也在这里进行。
onSurfaceChanged():当surface的尺寸发生改变时该方法被调用。我们可以在这里设置视口。若你的 camera 是固定的,也可以在这里设置 camera。
onDrawFrame():每帧都通过该方法进行绘制。绘制时通常先调用glClear函数来清空 framebuffer,然后在调用OpenGL ES的起它的接口进行绘制。
具体实现如代码清单13-1所示,省略部分代码,(全部代码已整理)
代码清单13-1:GLRenderer片段
view plaincopy to clipboardprint?
01.package com.oger.demo.box2d.activity;??
02.?
03.import javax.microedition.khronos.egl.EGLConfig;??
04.import javax.microedition.khronos.opengles.GL10;??
05.?
06.import org.jbox2d.collision.CircleShape;??
07.import org.jbox2d.collision.PolygonShape;??
08.import org.jbox2d.collision.Shape;??
09.import org.jbox2d.collision.ShapeType;??
10.import org.jbox2d.common.Vec2;??
11.import org.jbox2d.dynamics.Body;??
12.?
13.import android.content.Context;??
14.import android.opengl.GLSurfaceView;??
15.import android.opengl.GLU;??
16.import android.view.MotionEvent;??
17.?
18.public class GLRenderer implements GLSurfaceView.Renderer {??
19.??? // 窗口的宽度和高度??
20.??? private int width;??
21.??? private int height;??
22.?
23.??? // 选择激活的模型??
24.??? public void switchModel() {??
25.??????? activeModel++;??
26.??????? if (activeModel > 2) {??
27.??????????? activeModel = 0;??
28.??????? }??
29.??? }??
30.?
31.??? // 用于切换编辑模式与游戏模式??
32.??? public void toggleEdit() {??
33.??????? if (editMode == false) {??
34.??????????? editMode = true;??
35.??????? } else {??
36.??????????? editMode = false;??
37.??????? }??
38.??? }??
39.?
40.??? public void onSurfaceChanged(GL10 gl, int w, int h) {??
41.??????? // 设置视口??
42.??????? gl.glViewport(0, 0, w, h);??
43.??? }??
44.?
45.??? public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {??
46.??????? // 设置为正交视口??
47.??????? GLU.gluOrtho2D(gl, -12f, 12f, -20f, 20f);??
48.??????? // 允许顶点数组和纹理坐标数组??
49.??????? gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);??
50.??????? gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);??
51.??????? // 设置纹理映射方式??
52.??????? gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,??
53.??????????????? GL10.GL_REPEAT);??
54.??????? gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,??
55.??????????????? GL10.GL_REPEAT);??
56.??? }??
57.?
58.??? // 设置尺寸??
59.??? public void setSize(int x, int y) {??
60.??????? this.width = x;??
61.??????? this.height = y;??
62.??? }??
63.?
64.??? // 物理世界??
65.??? private PhysicsWorld mWorld;??
66.??? // box对象??
67.??? private DrawObject mBox;??
68.??? // 矩形条对象??
69.??? private DrawObject mLongBox;??
70.??? // 圆形对象??
71.??? private DrawObject mCircle;??
72.??? // 当前选择的模型??
73.??? private int activeModel = 1;??
74.??? // 是否处于编辑状态??
75.??? private boolean editMode = false;??
76.??? // Context,用于装载资源??
77.??? private Context mContext;??
78.??? // 保存鼠标的坐标??
79.??? private float startX, endX, startY, endY;??
80.?
81.??? public GLRenderer(Context newContext) {??
82.??????? mContext = newContext;??
83.??????? // 定一个box??
84.??????? mBox = new DrawObject(??
85.??????????????? new float[] { -1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0 },???
86.??????????????? new float[] { 0f, 1f, 1f, 1f, 1f, 0f, 0f, 0f },??
87.??????????????? new short[] { 0, 1, 2, 3, 0 }, 5);??
88.??????? // 定义一个长条??
89.??????? mLongBox = new DrawObject(??
90.??????????????? new float[] { -.2f, -2f, 0, .2f, -2f, 0, .2f,2f, 0, -.2f, 2f, 0 },???
91.??????????????? new float[] { 0f, 1f, 1f, 1f, 1f, 0f, 0f,0f },???
92.??????????????? new short[] { 0, 1, 2, 3, 0 }, 5);??
93.??????? // 圆形??
94.??????? mCircle = new DrawObject(new float[] { 0f, 0f, 0f, 0f, 1f, 0f, -.5f,??
95.??????????????? .866f, 0f, -.866f, .5f, 0f, -1f, 0f, 0f, -.866f, -.5f, 0f,??
96.??????????????? -.5f, -.866f, 0f, 0f, -1f, 0f, .5f, -.866f, 0f, .866f, -.5f,??
97.??????????????? 0f, 1f, 0f, 0f, .866f, .5f, 0f, .5f, .866f, 0f, 0f, 1f, 0f },??
98.??????????????? new float[] { 0.5f, 0.5f, 0.5f, 0.0f, .25f, .067f, .067f, .25f,??
99.??????????????????????? 0.0f, 0.5f, .067f, .75f, .25f, .933f, 0.5f, 1.0f, .75f,??
100.??????????????????????? .933f, .933f, .75f, 1.0f, 0.5f, .933f, .25f, .75f,??
101.??????????????????????? .067f, .5f, .0f },???
102.??????????????? new short[] { 0, 1, 2, 3, 4, 5, 6,??
103.??????????????????????? 7, 8, 9, 10, 11, 12, 13, 14 }, 14);??
104.??????? // 创建一个物理场景??
105.??????? mWorld = new PhysicsWorld();??
106.??????? mWorld.createWorld();??
107.??????? // 向物理场景添加两个实体??
108.??????? mWorld.addBox(0f, -25f, 50f, 10f, 0f, false);??
109.??????? mWorld.addBall(0f, -15f, 7f, false);??
110.??????? mWorld.addBox(-12f, -25f, 0.6f, 40f, 0f, false);??
111.??????? mWorld.addBox(12f, -25f, 0.6f, 40f, 0f, false);??
112.??? }??
113.?
114.??? // 绘制当前选择的模型??
115.??? public void drawActiveBody(GL10 gl) {??
116.??????? float x = 10f;??
117.??????? float y = 17f;??
118.??????? switch (activeModel) {??
119.??????? case 0:??
120.??????????? mCircle.draw(gl, x, y, 0f);??
121.??????????? break;??
122.??????? case 1:??
123.??????????? mBox.draw(gl, x, y, 0f);??
124.??????????? break;??
125.??????? case 2:??
126.??????????? mLongBox.draw(gl, x, y, 0f);??
127.??????????? break;??
128.??????? }??
129.??? }??
130.?
131.??? public void onDrawFrame(GL10 gl) {??
132.??????? if (editMode) {??
133.??????????? // 编辑模式则渲染为红色背景??
134.??????????? gl.glClearColor(0.5f, 0, 0f, 1.0f);??
135.??????? } else {??
136.??????????? // 游戏模式则渲染为蓝色??
137.??????????? gl.glClearColor(0f, 0, 0.5f, 1.0f);??
138.??????? }??
139.??????? // 清理颜色缓冲区和深度缓冲区??
140.??????? gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);??
141.??????? // 设置材质的混合模式??
142.??????? gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,??
143.??????????????? GL10.GL_REPLACE);??
144.??????? // 绘制游戏模式下的当前选择的模型??
145.??????? if (!editMode) {??
146.??????????? drawActiveBody(gl);??
147.??????? }??
148.??????? // 绘制编辑模式时的矩形条??
149.??????? if (editMode) {??
150.??????????? float midX = (startX + endX) / 2f;??
151.??????????? float midY = (startY + endY) / 2f;??
152.??????????? float sizeX = (endX - midX);??
153.??????????? float sizeY = (endY - midY);??
154.??????????? float rotate = (float) Math.atan((double) (sizeY / sizeX));??
155.??????????? float size = (float) Math??
156.??????????????????? .sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));??
157.??????????? mBox.draw(gl, midX, midY, 0f, rotate * 57.2957795f, size, .2f);??
158.??????? }??
159.??????? Vec2 vec;??
160.??????? // 得到世界场景中的实体列表??
161.??????? Body mBody = mWorld.getBodyList();??
162.??????? do {??
163.??????????? // 取得该实体的形状列表??
164.??????????? Shape mShape = mBody.getShapeList();??
165.??????????? if (mShape != null) {??
166.??????????????? vec = mBody.getPosition();??
167.??????????????? float rot = mBody.getAngle() * 57f;??
168.??????????????? // 弧度转化为角度??
169.??????????????? if (ShapeType.POLYGON_SHAPE == mShape.getType()) {??
170.??????????????????? Vec2[] vertexes = ((PolygonShape) mShape).getVertices();??
171.??????????????????? mBox.draw(gl, vec.x, vec.y, 0f, rot, vertexes[2].x,??
172.??????????????????????????? vertexes[2].y);??
173.??????????????? } else if (ShapeType.CIRCLE_SHAPE == mShape.getType()) {??
174.??????????????????? float radius = ((CircleShape) mShape).m_radius;??
175.??????????????????? mCircle.draw(gl, vec.x, vec.y, 0f, rot, radius);??
176.??????????????? }??
177.??????????? }??
178.??????????? // 取得下一个实体??
179.??????????? mBody = mBody.getNext();??
180.??????? } while (mBody != null);??
181.??????? // 更新场景???
182.??????? mWorld.update();???
183.??????? }??
184.?
185.??? // 在编辑模式时添加一条线??
186.??? public void addLine() {??
187.??????? float midX = (startX + endX) / 2f;??
188.??????? float midY = (startY + endY) / 2f;??
189.??????? float sizeX = (endX - midX);??
190.??????? float sizeY = (endY - midY);??
191.??????? float rotate = (float) Math.atan((double) (sizeY / sizeX));??
192.??????? float size = (float) Math??
193.??????????????? .sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));??
194.??????? // 添加一个长的box??
195.??????? mWorld.addBox(midX, midY, size, .2f, rotate, false);??
196.??????? startX = 0;??
197.??????? startY = 0;??
198.??????? endX = 0;??
199.??????? endY = 0;??
200.??? }??
201.?
202.??? public void touchEvent(float x, float y, int eventCode) {??
203.??????? // 计算x,y对应的场景坐标??
204.??????? float worldX = ((x - (this.width / 2)) * 12f) / (this.width / 2);??
205.??????? float worldY = ((y - (this.height / 2)) * -20f) / (this.height / 2);??
206.??????? if (!editMode) {??
207.??????????? // 当鼠标松开时,添加一个指定的实体??
208.??????????? if (eventCode == MotionEvent.ACTION_UP) {??
209.??????????????? switch (activeModel) {??
210.??????????????? case 0:??
211.??????????????????? mWorld.addBall(worldX, worldY, 0.98f, true);??
212.??????????????????? break;??
213.??????????????? case 1:??
214.??????????????????? mWorld.addBox(worldX, worldY, .98f, .98f, 0f, true);??
215.??????????????????? break;??
216.??????????????? case 2:??
217.??????????????????? mWorld.addBox(worldX, worldY, .2f, 2f, 0f, true);??
218.??????????????????? break;??
219.??????????????? }??
220.??????????? }??
221.??????? } else {??
222.??????????? // 确定要添加的线的坐标??
223.??????????? if (eventCode == MotionEvent.ACTION_DOWN) {??
224.??????????????? startX = worldX;??
225.??????????????? startY = worldY;??
226.??????????????? endX = worldX;??
227.??????????????? endY = worldY;??
228.??????????? } else if (eventCode == MotionEvent.ACTION_MOVE) {??
229.??????????????? endX = worldX;??
230.??????????????? endY = worldY;??
231.??????????? } else if (eventCode == MotionEvent.ACTION_UP) {??
232.??????????????? endX = worldX;??
233.??????????????? endY = worldY;??
234.??????????????? addLine();??
235.??????????? }??
236.??????? }??
237.??? }??
238.}?
package com.oger.demo.box2d.activity;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import org.jbox2d.collision.CircleShape;
import org.jbox2d.collision.PolygonShape;
import org.jbox2d.collision.Shape;
import org.jbox2d.collision.ShapeType;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.view.MotionEvent;
public class GLRenderer implements GLSurfaceView.Renderer {
?// 窗口的宽度和高度
?private int width;
?private int height;
?// 选择激活的模型
?public void switchModel() {
??activeModel++;
??if (activeModel > 2) {
???activeModel = 0;
??}
?}
?// 用于切换编辑模式与游戏模式
?public void toggleEdit() {
??if (editMode == false) {
???editMode = true;
??} else {
???editMode = false;
??}
?}
?public void onSurfaceChanged(GL10 gl, int w, int h) {
??// 设置视口
??gl.glViewport(0, 0, w, h);
?}
?public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
??// 设置为正交视口
??GLU.gluOrtho2D(gl, -12f, 12f, -20f, 20f);
??// 允许顶点数组和纹理坐标数组
??gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
??gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
??// 设置纹理映射方式
??gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
????GL10.GL_REPEAT);
??gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
????GL10.GL_REPEAT);
?}
?// 设置尺寸
?public void setSize(int x, int y) {
??this.width = x;
??this.height = y;
?}
?// 物理世界
?private PhysicsWorld mWorld;
?// box对象
?private DrawObject mBox;
?// 矩形条对象
?private DrawObject mLongBox;
?// 圆形对象
?private DrawObject mCircle;
?// 当前选择的模型
?private int activeModel = 1;
?// 是否处于编辑状态
?private boolean editMode = false;
?// Context,用于装载资源
?private Context mContext;
?// 保存鼠标的坐标
?private float startX, endX, startY, endY;
?public GLRenderer(Context newContext) {
??mContext = newContext;
??// 定一个box
??mBox = new DrawObject(
????new float[] { -1, -1, 0, 1, -1, 0, 1, 1, 0, -1,?1, 0 },
????new float[] { 0f, 1f, 1f, 1f, 1f, 0f, 0f, 0f },
????new short[] { 0, 1, 2, 3, 0 }, 5);
??// 定义一个长条
??mLongBox = new DrawObject(
????new float[] { -.2f, -2f, 0, .2f, -2f, 0, .2f,2f, 0, -.2f, 2f, 0 },
????new float[] { 0f, 1f, 1f, 1f, 1f, 0f, 0f,0f },
????new short[] { 0, 1, 2, 3, 0 }, 5);
??// 圆形
??mCircle = new DrawObject(new float[] { 0f, 0f, 0f, 0f, 1f, 0f, -.5f,
????.866f, 0f, -.866f, .5f, 0f, -1f, 0f, 0f, -.866f, -.5f, 0f,
????-.5f, -.866f, 0f, 0f, -1f, 0f, .5f, -.866f, 0f, .866f, -.5f,
????0f, 1f, 0f, 0f, .866f, .5f, 0f, .5f, .866f, 0f, 0f, 1f, 0f },
????new float[] { 0.5f, 0.5f, 0.5f, 0.0f, .25f, .067f, .067f, .25f,
??????0.0f, 0.5f, .067f, .75f, .25f, .933f, 0.5f, 1.0f, .75f,
??????.933f, .933f, .75f, 1.0f, 0.5f, .933f, .25f, .75f,
??????.067f, .5f, .0f },
????new short[] { 0, 1, 2, 3, 4, 5, 6,
??????7, 8, 9, 10, 11, 12, 13, 14 }, 14);
??// 创建一个物理场景
??mWorld = new PhysicsWorld();
??mWorld.createWorld();
??// 向物理场景添加两个实体
??mWorld.addBox(0f, -25f, 50f, 10f, 0f, false);
??mWorld.addBall(0f, -15f, 7f, false);
??mWorld.addBox(-12f, -25f, 0.6f, 40f, 0f, false);
??mWorld.addBox(12f, -25f, 0.6f, 40f, 0f, false);
?}
?// 绘制当前选择的模型
?public void drawActiveBody(GL10 gl) {
??float x = 10f;
??float y = 17f;
??switch (activeModel) {
??case 0:
???mCircle.draw(gl, x, y, 0f);
???break;
??case 1:
???mBox.draw(gl, x, y, 0f);
???break;
??case 2:
???mLongBox.draw(gl, x, y, 0f);
???break;
??}
?}
?public void onDrawFrame(GL10 gl) {
??if (editMode) {
???// 编辑模式则渲染为红色背景
???gl.glClearColor(0.5f, 0, 0f, 1.0f);
??} else {
???// 游戏模式则渲染为蓝色
???gl.glClearColor(0f, 0, 0.5f, 1.0f);
??}
??// 清理颜色缓冲区和深度缓冲区
??gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
??// 设置材质的混合模式
??gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
????GL10.GL_REPLACE);
??// 绘制游戏模式下的当前选择的模型
??if (!editMode) {
???drawActiveBody(gl);
??}
??// 绘制编辑模式时的矩形条
??if (editMode) {
???float midX = (startX + endX) / 2f;
???float midY = (startY + endY) / 2f;
???float sizeX = (endX - midX);
???float sizeY = (endY - midY);
???float rotate = (float) Math.atan((double) (sizeY / sizeX));
???float size = (float) Math
?????.sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));
???mBox.draw(gl, midX, midY, 0f, rotate * 57.2957795f, size, .2f);
??}
??Vec2 vec;
??// 得到世界场景中的实体列表
??Body mBody = mWorld.getBodyList();
??do {
???// 取得该实体的形状列表
???Shape mShape = mBody.getShapeList();
???if (mShape != null) {
????vec = mBody.getPosition();
????float rot = mBody.getAngle() * 57f;
????// 弧度转化为角度
????if (ShapeType.POLYGON_SHAPE == mShape.getType()) {
?????Vec2[] vertexes = ((PolygonShape) mShape).getVertices();
?????mBox.draw(gl, vec.x, vec.y, 0f, rot, vertexes[2].x,
???????vertexes[2].y);
????} else if (ShapeType.CIRCLE_SHAPE == mShape.getType()) {
?????float radius = ((CircleShape) mShape).m_radius;
?????mCircle.draw(gl, vec.x, vec.y, 0f, rot, radius);
????}
???}
???// 取得下一个实体
???mBody = mBody.getNext();
??} while (mBody != null);
??// 更新场景
??mWorld.update();
??}
?// 在编辑模式时添加一条线
?public void addLine() {
??float midX = (startX + endX) / 2f;
??float midY = (startY + endY) / 2f;
??float sizeX = (endX - midX);
??float sizeY = (endY - midY);
??float rotate = (float) Math.atan((double) (sizeY / sizeX));
??float size = (float) Math
????.sqrt((double) ((sizeX * sizeX) + (sizeY * sizeY)));
??// 添加一个长的box
??mWorld.addBox(midX, midY, size, .2f, rotate, false);
??startX = 0;
??startY = 0;
??endX = 0;
??endY = 0;
?}
?public void touchEvent(float x, float y, int eventCode) {
??// 计算x,y对应的场景坐标
??float worldX = ((x - (this.width / 2)) * 12f) / (this.width / 2);
??float worldY = ((y - (this.height / 2)) * -20f) / (this.height / 2);
??if (!editMode) {
???// 当鼠标松开时,添加一个指定的实体
???if (eventCode == MotionEvent.ACTION_UP) {
????switch (activeModel) {
????case 0:
?????mWorld.addBall(worldX, worldY, 0.98f, true);
?????break;
????case 1:
?????mWorld.addBox(worldX, worldY, .98f, .98f, 0f, true);
?????break;
????case 2:
?????mWorld.addBox(worldX, worldY, .2f, 2f, 0f, true);
?????break;
????}
???}
??} else {
???// 确定要添加的线的坐标
???if (eventCode == MotionEvent.ACTION_DOWN) {
????startX = worldX;
????startY = worldY;
????endX = worldX;
????endY = worldY;
???} else if (eventCode == MotionEvent.ACTION_MOVE) {
????endX = worldX;
????endY = worldY;
???} else if (eventCode == MotionEvent.ACTION_UP) {
????endX = worldX;
????endY = worldY;
????addLine();
???}
??}
?}
}
?
?
在OpenGL初始化完成之后,我们应该进行一些视图设置。首先是设定视见区域,即告诉OpenGL应把渲染之后的图形绘制在窗体的哪个部位。当视见区域是整个窗体时,OpenGL将把渲染结果绘制到整个窗口。我们可以调用glViewPort函数来决定视见区域;在onSurfaceChanged函数中我们通过glViewport设置了Opengl ES的视口,其中参数X,Y指定了视见区域的左下角在窗口中的位置,一般情况下为(0,0),Width和Height指定了视见区域的宽度和高度。注意OpenGL使用的窗口坐标和Android使用的窗口坐标是不一样的。Opengl使用的窗口坐标的远点位于屏幕左下角。
在onSurfaceCreated函数中,首先通过GLU.gluOrtho2D函数设置二维坐标系统参数,函数有4个参数,可以理解为用该函数设置后,这个二维坐标系的左上角的坐标为(left,top),右下角的坐标为(right,bottom)。如果保持画图的参数不变,将左上角和右下角表示的范围扩大,则图像看起来就缩小了,反之就放大了,这些坐标同样以左下角作为坐标原点。
然后,由于我们绘制这些2D的物体时,需要使用顶点数组和纹理坐标数组,所以通过glEnableClientState函数和参数GL_VERTEX_ARRAY和GL_TEXTURE_COORD_ARRAY分别打开了允许设置顶点数组和纹理坐标数组,稍后再具体绘制时,大家会看到我们如何设置顶点数组和纹理数组的。一般情况我们将图象从纹理图象空间映射到帧缓冲图象空间(映射需要重新构造纹理图像,这样就会造成应用到多边形上的图像失真),这时我们就可用glTexParmeterx()函数来确定如何把纹理象素映射成像素,即纹理映射的方式,通常还有glTexParmeteri函数等,因为opengl提供了几套不同数据类型的函数来完成通一个功能,其中第一个参数GL_TEXTURE_2D表示我们将操作的是2D纹理,因为Opengl支持一维纹理、二维纹理,但是Opengl ES只支持二维纹理,所以第一个参数不会怎么变化,后面的参数通常是需要进行组合的,主要有以下几个参数可以选择设置:
GL_TEXTURE_WRAP_S: S方向上的贴图模式
GL_CLAMP: 将纹理坐标限制在0.0,1.0的范围之内。边缘将会拉伸填充。
GL_TEXTURE_MAG_FILTER: 放大过滤
GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值
GL_TEXTURE_MIN_FILTER: 缩小过滤
GL_LINEAR_MIPMAP_NEAREST: 使用GL_NEAREST对最接近当前多边形的解析度的两个层级贴图进行采样,然后用这两个值进行线性插值
最后,代码中定义了activeModel和editMode分别表示当前所选择激活的模型,和是否处于编辑状态,然后分别通过函数switchModel和toggleEdit来控制操作者两个状态,逻辑非常简单,大家看看代码就明白了,我们就不多浪费时间了。其中的变量width和height主要是表示窗口的宽度和高度,大家可能已经注意到在GameGLSurfaceView中的onTouchEvent函数中,我们每次都调用GLRenderer的setSize函数来设置了窗口的宽度和高度,在GLRenderer中同样是用于触摸事件的处理用,主要是将触摸坐标转换为场景中的坐标,稍后我们会介绍如何转换。到这里我们就实现了GLRenderer的一部分,下面我们开始学习如何在这里来使用JBox2d了,后面还会介绍在GLRenderer中整个该物理引擎的使用。
使用Opengl绘制图形—rawObject)
DrawObject主要用于替代JBox2d中的图像渲染部分,但是这里我们只是实现了绘制矩形和圆形,还有更多的没有实现,该游戏中也将主要使用这样两种形状。DrawObject的实现几乎就全是Opengl相关的内容,我们先看具体代码,在来分析,如代码清单13-2所示。
代码清单13-2:DrawObject.java
view plaincopy to clipboardprint?
01.import java.nio.ByteBuffer;??
02.import java.nio.ByteOrder;??
03.import java.nio.FloatBuffer;??
04.import java.nio.ShortBuffer;??
05.?
06.import javax.microedition.khronos.opengles.GL10;??
07.?
08.import android.content.Context;??
09.import android.graphics.Bitmap;??
10.import android.graphics.BitmapFactory;??
11.import android.opengl.GLUtils;??
12.?
13.public class DrawObject {??
14.??? // 顶点缓冲区??
15.??? private FloatBuffer mVertexBuffer;??
16.??? // 索引缓冲区??
17.??? private ShortBuffer mIndexBuffer;??
18.??? // 纹理坐标缓冲区??
19.??? private FloatBuffer mTexBuffer;??
20.??? // 顶点计数??
21.??? private int vertexCount = 0;??
22.??? // 是否拥有贴图??
23.??? private boolean hasTexture = false;??
24.??? // 纹理??
25.??? private int[] mTexture = new int[1];??
26.?
27.??? // float[]->FloatBuffer??
28.??? protected static FloatBuffer makeFloatBuffer(float[] arr) {??
29.??????? ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);??
30.??????? bb.order(ByteOrder.nativeOrder());??
31.??????? FloatBuffer fb = bb.asFloatBuffer();??
32.??????? fb.put(arr);??
33.??????? fb.position(0);??
34.??????? return fb;??
35.??? }??
36.?
37.??? // short[]->ShortBuffer??
38.??? protected static ShortBuffer makeShortBuffer(short[] arr) {??
39.??????? ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);??
40.??????? bb.order(ByteOrder.nativeOrder());??
41.??????? ShortBuffer ib = bb.asShortBuffer();??
42.??????? ib.put(arr);??
43.??????? ib.position(0);??
44.??????? return ib;??
45.??? }??
46.?
47.??? // 构造(顶点数组,纹理数组,索引数组,顶点数)??
48.??? public DrawObject(float[] coords, float[] tcoords, short[] icoords,??
49.??????????? int vertexes) {??
50.??????? this(coords, icoords, vertexes);??
51.??????? mTexBuffer = makeFloatBuffer(tcoords);??
52.??? }??
53.?
54.??? // 构造(顶点数组,索引数组,顶点数)??
55.??? public DrawObject(float[] coords, short[] icoords, int vertexes) {??
56.??????? vertexCount = vertexes;??
57.??????? mVertexBuffer = makeFloatBuffer(coords);??
58.??????? mIndexBuffer = makeShortBuffer(icoords);??
59.??? }??
60.?
61.??? // 装载贴图(gl,context,资源id)??
62.??? public void loadTexture(GL10 gl, Context mContext, int mTex) {??
63.??????? hasTexture = true;??
64.??????? // 生成纹理??
65.??????? gl.glGenTextures(1, mTexture, 0);??
66.??????? // 绑定纹理??
67.??????? gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);??
68.??????? // 资源Bitmap??
69.??????? Bitmap bitmap;??
70.??????? bitmap = BitmapFactory.decodeResource(mContext.getResources(), mTex);??
71.??????? // 指定纹理图像??
72.??????? GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);??
73.??????? bitmap.recycle();??
74.??????? // 设置纹理参数??
75.??????? gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,??
76.??????????????? GL10.GL_LINEAR);??
77.??????? gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,??
78.??????????????? GL10.GL_LINEAR);??
79.??? }??
80.?
81.??? /*----------------------*/?
82.??? // 渲染obj??
83.??? /*----------------------*/?
84.??? public void draw(GL10 gl) {??
85.??????? // 判断是否拥有纹理??
86.??????? if (hasTexture) {??
87.??????????? // 打开2d纹理??
88.??????????? gl.glEnable(GL10.GL_TEXTURE_2D);??
89.??????????? // 绑定纹理??
90.??????????? gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);??
91.??????????? // 设置纹理缓冲区??
92.??????????? gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);??
93.??????? } else {??
94.??????????? // 关闭2d纹理??
95.??????????? gl.glDisable(GL10.GL_TEXTURE_2D);??
96.??????? }??
97.??????? // 设置逆时针方向为正面??
98.??????? gl.glFrontFace(GL10.GL_CCW);??
99.??????? // 设置顶点数组??
100.??????? gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);??
101.??????? // 绘制??
102.??????? gl.glDrawElements(GL10.GL_TRIANGLE_FAN, vertexCount,??
103.??????????????? GL10.GL_UNSIGNED_SHORT, mIndexBuffer);??
104.??????? // 关闭2d纹理??
105.??????? gl.glDisable(GL10.GL_TEXTURE_2D);??
106.??? }??
107.?
108.??? public void draw(GL10 gl, float x, float y, float z, float rot, float scale) {??
109.??????? this.draw(gl, x, y, z, rot, scale, scale);??
110.??? }??
111.?
112.??? // draw(gl,x,y,z,旋转角度,x方向缩放,y方向缩放)??
113.??? public void draw(GL10 gl, float x, float y, float z, float rot,??
114.??????????? float scaleX, float scaleY) {??
115.??????? gl.glPushMatrix();??
116.??????? gl.glTranslatef(x, y, z);??
117.??????? gl.glRotatef(rot, 0f, 0f, 1f);??
118.??????? gl.glScalef(scaleX, scaleY, 1f);??
119.??????? this.draw(gl);??
120.??????? gl.glPopMatrix();??
121.??? }??
122.?
123.??? public void draw(GL10 gl, float x, float y, float z, float rot) {??
124.??????? gl.glPushMatrix();??
125.??????? gl.glTranslatef(x, y, z);??
126.??????? gl.glRotatef(rot, 0f, 0f, 1f);??
127.??????? this.draw(gl);??
128.??????? gl.glPopMatrix();??
129.??? }??
130.?
131.??? public void draw(GL10 gl, float x, float y, float z) {??
132.??????? gl.glPushMatrix();??
133.??????? gl.glTranslatef(x, y, z);??
134.??????? this.draw(gl);??
135.??????? gl.glPopMatrix();??
136.??? }??
137.}?
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
public class DrawObject {
?// 顶点缓冲区
?private FloatBuffer mVertexBuffer;
?// 索引缓冲区
?private ShortBuffer mIndexBuffer;
?// 纹理坐标缓冲区
?private FloatBuffer mTexBuffer;
?// 顶点计数
?private int vertexCount = 0;
?// 是否拥有贴图
?private boolean hasTexture = false;
?// 纹理
?private int[] mTexture = new int[1];
?// float[]->FloatBuffer
?protected static FloatBuffer makeFloatBuffer(float[] arr) {
??ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
??bb.order(ByteOrder.nativeOrder());
??FloatBuffer fb = bb.asFloatBuffer();
??fb.put(arr);
??fb.position(0);
??return fb;
?}
?// short[]->ShortBuffer
?protected static ShortBuffer makeShortBuffer(short[] arr) {
??ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
??bb.order(ByteOrder.nativeOrder());
??ShortBuffer ib = bb.asShortBuffer();
??ib.put(arr);
??ib.position(0);
??return ib;
?}
?// 构造(顶点数组,纹理数组,索引数组,顶点数)
?public DrawObject(float[] coords, float[] tcoords, short[] icoords,
???int vertexes) {
??this(coords, icoords, vertexes);
??mTexBuffer = makeFloatBuffer(tcoords);
?}
?// 构造(顶点数组,索引数组,顶点数)
?public DrawObject(float[] coords, short[] icoords, int vertexes) {
??vertexCount = vertexes;
??mVertexBuffer = makeFloatBuffer(coords);
??mIndexBuffer = makeShortBuffer(icoords);
?}
?// 装载贴图(gl,context,资源id)
?public void loadTexture(GL10 gl, Context mContext, int mTex) {
??hasTexture = true;
??// 生成纹理
??gl.glGenTextures(1, mTexture, 0);
??// 绑定纹理
??gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);
??// 资源Bitmap
??Bitmap bitmap;
??bitmap = BitmapFactory.decodeResource(mContext.getResources(), mTex);
??// 指定纹理图像
??GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
??bitmap.recycle();
??// 设置纹理参数
??gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
????GL10.GL_LINEAR);
??gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
????GL10.GL_LINEAR);
?}
?/*----------------------*/
?// 渲染obj
?/*----------------------*/
?public void draw(GL10 gl) {
??// 判断是否拥有纹理
??if (hasTexture) {
???// 打开2d纹理
???gl.glEnable(GL10.GL_TEXTURE_2D);
???// 绑定纹理
???gl.glBindTexture(GL10.GL_TEXTURE_2D, mTexture[0]);
???// 设置纹理缓冲区
???gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);
??} else {
???// 关闭2d纹理
???gl.glDisable(GL10.GL_TEXTURE_2D);
??}
??// 设置逆时针方向为正面
??gl.glFrontFace(GL10.GL_CCW);
??// 设置顶点数组
??gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
??// 绘制
??gl.glDrawElements(GL10.GL_TRIANGLE_FAN, vertexCount,
????GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
??// 关闭2d纹理
??gl.glDisable(GL10.GL_TEXTURE_2D);
?}
?public void draw(GL10 gl, float x, float y, float z, float rot, float scale) {
??this.draw(gl, x, y, z, rot, scale, scale);
?}
?// draw(gl,x,y,z,旋转角度,x方向缩放,y方向缩放)
?public void draw(GL10 gl, float x, float y, float z, float rot,
???float scaleX, float scaleY) {
??gl.glPushMatrix();
??gl.glTranslatef(x, y, z);
??gl.glRotatef(rot, 0f, 0f, 1f);
??gl.glScalef(scaleX, scaleY, 1f);
??this.draw(gl);
??gl.glPopMatrix();
?}
?public void draw(GL10 gl, float x, float y, float z, float rot) {
??gl.glPushMatrix();
??gl.glTranslatef(x, y, z);
??gl.glRotatef(rot, 0f, 0f, 1f);
??this.draw(gl);
??gl.glPopMatrix();
?}
?public void draw(GL10 gl, float x, float y, float z) {
??gl.glPushMatrix();
??gl.glTranslatef(x, y, z);
??this.draw(gl);
??gl.glPopMatrix();
?}
}
?
代码并不多,其实一个DrawObject对象将代表一个物体,通常绘制一个物体,主要包括顶点缓冲区(mVertexBuffer),索引缓冲区(mIndexBuffer),纹理坐标缓冲区(mTexBuffer),同时我们通过vertexCount来记录顶点的个数,hasTexture检测是否有纹理,没有就将使用颜色作为纹理,mTexture数组就表示纹理ID。
其中有两个静态函数makeFloatBuffer和makeShortBuffer用于将数组转换成对应的缓冲区,在Android中使用java来编写Opengl ES程序,在传递顶点等数组时需要使用缓冲区,转换过程很简单,首先构建一个和数组一样的缓冲区,然后检索该缓冲区的字节顺序,最后将数组作为缓冲区即可。
构造函数很简单,将绘制该图形所需要数据传入即可,两个构造函数,其中一个就说明了当没有纹理坐标时,我们就不需要设置纹理坐标缓冲区,直接通过颜色来作为材质即可。
装载纹理需要使用loadTexture函数,首先将hasTexture设置为有纹理存在,glGenTextures函数用于根据纹理参数返回n个纹理名称,用来生成纹理名字的数量、存储纹理名称数组、以及存放在纹理数组中的偏移量。glBindTexture函数实现了将调用glGenTextures函数生成的纹理的名字绑定到对应的目标纹理上,其参数分别是:纹理被绑定的目标(在Opengl ES中它只能取值GL_TEXTURE_2D)和纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。然后通过BitmapFactory.decodeResource取得纹理图片资源,然后通过GLUtils.texImage2D函数将纹理图片像素数据绑定到Opengl对象中。GLUtils.texImage2D函数参数的含义如下:
target:指定目标纹理,必须为GL_TEXTURE_2D
level:指定图像级别的编号,0表示基本图像
bitmap:纹理图片数据
border:纹理图像的边框宽度,必须是0或1
如果我们使用glTexImage2D函数,用来指定二维纹理图像,他还有以下几个参数可以使用:
components:纹理中颜色组件的编号,可是是1或2或3或4
width:纹理图像的宽度
height:纹理图像的高度
format:指定像素数据的格式,一共有9个取值:GL_COLOR_INDEX、GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_RGB、GL_RGBA、GL_BGR_EXT、GL_BGRA_EXT、GL_LUMINANCE、GL_LUMINANCE_ALPHA
type:像素数据的数据类型,取值可以为GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, and GL_FLOAT
pixels:内存中像素数据的指针
设置好纹理图像数据之后,我们需要使用recycle来将该图片数据释放掉,因为在opengl es中它将自己保存一份纹理数据。接着需要设置纹理参数,可以使用glTexParameteri函数或者glTexParameterf函数,其参数的含义如下:
arget:目标纹理,必须为GL_TEXTURE_1D或GL_TEXTURE_2D;
pname:用来设置纹理映射过程中像素映射的问题等,取值可以为:GL_TEXTURE_MIN_FILTER、GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T
param:实际上就是pname的值
另外还可以使用如下代码,实现线形滤波的功能,当纹理映射到图形表面以后,如果因为其它条件的设置导致纹理不能更好地显示的时候,进行过滤,按照指定的方式进行显示,可能会过滤掉显示不正常的纹理像素。
复制到剪贴板? Java代码01.glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR) glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)????
剩下的都是一些绘制函数了,用来根据不同的条件进行不同的渲染。最完整的渲染条件是包括:x,y,z,旋转角度,x方向缩放,y方向缩放。整个过程就是将矩阵压栈(glPushMatrix),然后进行变化,绘制等操作之后,在将矩阵弹出栈(glPopMatrix)。为什么要这样呢?因为我们在变换坐标的时候,使用的是glTranslatef(),glRotaef()等函数来操作,操作的是当前矩阵,这些变化,将会对矩阵进行相乘,使之改变了当前的矩阵,当我们再次使用该矩阵时,就已经被改变而变得很难控制了,所以,我们在进行变换操作之前都需要将将矩阵进行压栈保存起来,操作完成之后,弹出栈的矩阵则和操作前的矩阵一样。下面我们分析一下具体的绘制函数"public void draw(GL10 gl)"。
首先判断是否有纹理,如果有则通过glEnable(GL10.GL_TEXTURE_2D)打开2D纹理,然后通过glBindTexture来绑定纹理,前面我们允许设置了纹理坐标缓冲区,所以这里也需要通过glTexCoordPointer来设置纹理坐标缓冲区;如果没有纹理,则通过glDisable(GL10.GL_TEXTURE_2D)关闭2D纹理映射。然后通过glFrontFace来设置正面,分为逆时针和顺时针,glVertexPointer可以设置顶点缓冲区,使用glDrawElements进行渲染操作,渲染完成之后切忌关闭2D纹理映射。
glDrawElements函数参数定义如下:
mode:指定绘制图元的类型,它应该是下列值之一,GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES,???????? GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES
count:为绘制图元的数量
type:为索引值的类型,只能是下列值之一:GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT
indices:索引缓冲区
物理世界(PhysicsWorld)
看这个标题好像很复杂,的确物理世界很复杂,但是我们这