本节书摘来自异步社区《Android 应用案例开发大全(第3版)》一书中的第2章,第2.5节辅助绘制类,作者 吴亚峰 , 苏亚光 , 于复兴,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.5 辅助绘制类
上一节介绍了实现壁纸的开发,本节将对辅助绘制类的开发进行详细介绍。在绘制百纳水族馆动态壁纸中各个物体之前,必须要做好准备工作,而这些准备工作就包括辅助绘制类的开发。其中包括背景图辅助绘制类Background,气泡辅助绘制类Bubble,鱼类辅助绘制类MS3DModel以及模型辅助绘制类LoadedObjectVertexNormalTexture,下面就对这些类的开发进行详细的介绍。
2.5.1 背景辅助绘制类——Background
本小节将对本案例的背景辅助绘制类进行详细介绍,这个类的作用是绘制百纳水族馆的背景模型,在此逼真的深海背景下,所有的鱼、珍珠贝以及气泡都在此背景前呈现,使整个百纳水族馆更加地活灵活现。
(1)首先来看本类的框架结构,本类中包括对背景图的顶点坐标及纹理坐标的初始化方法,以及对着色器初始化的initShader()方法和drawSelf()方法。仔细学习本类的结构,有助于读者更快地掌握本类所讲的知识,对读者有很大的帮助。其具体代码如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/wyf/lxg/background/目录下的Background.java。
1 package wyf.lxg.background; //声明包名
2 ......//此处省略部分类和包的引入代码,读者可自行查阅随书光盘中的源代码
3 public class Background{
4 int mProgram; //自定义渲染管线程序id
5 ......//此处省略了本类中部分成员变量的声明,读者可自行查阅随书光盘中的源代码
6 public Background(MySurfaceView mv){
7 initVertexData(); //初始化顶点坐标与着色数据
8 initShader(mv); //初始化着色器
9 }
10 public void initVertexData(){ //初始化顶点坐标与着色数据的方法
11 //顶点坐标数据的初始化
12 vCount=6; //顶点的数量
13 float vertices[]=new float[] { //顶点坐标数据数组
14 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
15 -30*Constant.SCREEN_SCALEZ,
16 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
17 -30*Constant.SCREEN_SCALEZ,
18 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
19 -30*Constant.SCREEN_SCALEZ,
20 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
21 -30*Constant.SCREEN_SCALEZ,
22 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
23 -30*Constant.SCREEN_SCALEZ,
24 *Constant.SCREEN_SCALEX,*Constant.SCREEN_SCALEY,
25 -30*Constant.SCREEN_SCALEZ,
26 };
27 //创建顶点坐标数据缓冲
28 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
29 vbb.order(ByteOrder.nativeOrder()); //设置字节顺序
30 mVertexBuffer = vbb.asFloatBuffer(); //转换为Float型缓冲
31 mVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据
32 mVertexBuffer.position(0); //设置缓冲区起始位置
33 //创建纹理坐标缓冲
34 float textureCoors[]=new float[]{ //顶点纹理S、T坐标值数组
35 0,0,0, 1,1,0, 0,1,1, 1,1,0};
36 //创建顶点纹理数据缓冲
37 ByteBuffer cbb = ByteBuffer.allocateDirect(textureCoors.length*4);
38 cbb.order(ByteOrder.nativeOrder()); //设置字节顺序
39 mTexCoorBuffer = cbb.asFloatBuffer(); //转换为Float型缓冲
40 mTexCoorBuffer.put(textureCoors); //向缓冲区中放入顶点着色数据
41 mTexCoorBuffer.position(0); //设置缓冲区起始位置
42 }
43 ......//此处省略了部分源代码,将在后面的步骤中给出
44 ......//此处省略了部分源代码,将在后面的步骤中给出
45 }
第6~9行是这个类的构造方法,此方法调用初始化顶点坐标与着色数据的initVertexData()方法和初始化着色器的initShader(mv)方法。
第13~32行是对顶点坐标数据的初始化,用三角形卷绕方式创建一个背景模型,将顶点数据存到float类型的顶点数组中,并创建顶点坐标缓冲,同时对顶点字节进行设置,然后放入顶点坐标缓冲区,设置缓冲区的起始位置。
第33~41行是对创建好的背景模型进行纹理创建,对顶点纹理S、T坐标进行初始化,将顶点纹理坐标数据存入float类型的纹理数组中,并创建纹理坐标缓冲,对纹理坐标进行设置然后送入缓冲区并设置起始位置。
(2)读者对本类的框架掌握后,下面将为读者介绍本类中对着色器初始化的方法initShader()方法以及绘制矩形的drawSelf()方法。drawSelf()方法最后为画笔指定顶点位置数据和画笔指定顶点纹理坐标数据,绘制纹理矩形。其具体代码如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/wyf/lxg/background/目录下的Background.java。
1 public void initShader(MySurfaceView mv){ //初始化着色器
2 //加载顶点着色器的脚本内容
3 mVertexShader=ShaderUtil.loadFromAssetsFile("back_vertex.sh",
mv.getResources());
4 //加载片元着色器的脚本内容
5 mFragmentShader=ShaderUtil.loadFromAssetsFile("back_frag.sh",
mv.getResources());
6 //基于顶点着色器与片元着色器创建程序
7 mProgram = createProgram(mVertexShader, mFragmentShader);
8 //获取程序中顶点位置属性引用id
9 maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
10 //获取程序中顶点纹理坐标属性引用id
11 maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor");
12 //获取程序中总变换矩阵引用id
13 muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
14 }
15 public void drawSelf(int texId){
16 GLES20.glUseProgram(mProgram); //制定使用某套shader程序
17 //将最终变换矩阵传入shader程序
18 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.
getFinalMatrix(), 0);
19 GLES20.glVertexAttribPointer( maPositionHandle, 3, //为画笔指定顶点位置数据
20 GLES20.GL_FLOAT, false,3*4, mVertexBuffer );
21 GLES20.glVertexAttribPointer( maTexCoorHandle,2, //指定顶点纹理坐标数据
22 GLES20.GL_FLOAT, false,2*4, mTexCoorBuffer );
23 GLES20.glEnableVertexAttribArray(maPositionHandle); //允许顶点位置数据数组
24 GLES20.glEnableVertexAttribArray(maTexCoorHandle); //允许顶点纹理坐标数组
25 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); //绑定纹理
26 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
27 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); //绘制纹理矩形
28 }
第1~14行是本类中对着色器的初始化,将着色器脚本内容加载并基于其顶点与片元着色器来创建程序供显卡使用,并从程序中获取顶点位置属性、顶点纹理坐标属性、总变换矩阵属性的id引用来使用。
第15~28行是本类中的drawSlef()方法,其作用是绘制矩形,根据数据画出需要的矩形。首先制定某套shader程序,将一些数据传入shader程序,最后为画笔指定顶点位置数据和为画笔指定顶点纹理坐标数据,绘制纹理矩形。
2.5.2 气泡辅助绘制类——Bubble
本小节将对案例中的气泡辅助绘制类进行详细的介绍,在该壁纸的场景中,有三处气泡位置在不断地冒出透明的气泡,这些气泡上升的高度不同,并且这些气泡随着高度的不断增加,大小也在不断变大,要绘制这些气泡,首先需要构造气泡模型,其具体代码如下所示。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/wyf/lxg/bubble/目录下的Bubble.java。
1 package wyf.lxg.bubble; //声明包名
2 ......//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码
3 public class Bubble{
4 ......//此处省略了本类中部分成员变量的声明,读者可自行查阅随书光盘中的源代码
5 int vCount=0; //顶点的数量
6 public Bubble(MySurfaceView mv){
7 initVertexData(); //调用初始化顶点数据的initVertexData方法
8 initShader(mv); //调用初始化着色器的intShader方法
9 }
10 public void initVertexData(){ //顶点坐标数据的初始化
11 vCount=6; //顶点的数量
12 float vertices[]=new float[] { //顶点坐标数据数组
13 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
14 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
15 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
16 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
17 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
18 *Constant.UNIT_SIZE,*Constant.UNIT_SIZE,0,
19 };
20 //创建顶点坐标数据缓冲
21 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
22 vbb.order(ByteOrder.nativeOrder()); //设置字节顺序
23 mVertexBuffer = vbb.asFloatBuffer(); //转换为int型缓冲
24 mVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据
25 mVertexBuffer.position(0); //设置缓冲区起始位置
26 float textureCoors[]=new float[]{0,0,0,1,1,0, 0,1,1,1,1,0 };
//顶点纹理S、T坐标值数组
27 ByteBuffer cbb = ByteBuffer.allocateDirect(textureCoors.length*4);
//创建纹理数据缓冲
28 cbb.order(ByteOrder.nativeOrder()); //设置字节顺序
29 mTexCoorBuffer = cbb.asFloatBuffer(); //转换为int型缓冲
30 mTexCoorBuffer.put(textureCoors); //向缓冲区中放入顶点着色数据
31 mTexCoorBuffer.position(0); //设置缓冲区起始位置
32 }
33 ......//该处省略了与上节类似的initShader()方法,读者可自行查阅随书光盘中的源代码
34 ......// 该处省略了与上节类似的drawSlef()方法,读者可自行查阅随书光盘中的源代码
35 }
第6~9行是这个类的构造方法,调用initVertexData()方法对顶点坐标数据与顶点纹理坐标数据进行初始化,并调用initShader(mv)方法对着色器进行初始化。
第11~25行是对气泡模型的顶点坐标数据的初始化,首先以三角形卷绕的方式组装顶点数据并将数据存放到float类型的数组中,并创建顶点坐标数据缓冲,对顶点数据进行设置并放入缓冲区,设置缓冲区的起始位置。
第26~32行是对气泡模型的顶点纹理坐标S、T进行初始化,其坐标的组装需要与顶点卷绕方式、方向一致,并将顶点纹理坐标数据存入float类型的数组中,创建顶点纹理坐标缓冲,设置顶点纹理坐标,放入缓冲区,并设置缓冲区的起始位置。
说明
因其模型辅助绘制类LoadedObjectVertexNormalTexture与背景图辅助绘制类Background,气泡辅助绘制类Bubble中的代码类似,所以只对以上两个小节的辅助绘制类做详细介绍,其模型辅助绘制类LoadedObjectVertexNormalTexture读者可查看随书光盘中的源代码。
2.5.3 鱼类辅助绘制类——MS3DModel
本小节将对鱼类辅助绘制类MS3DModel进行详细的介绍,在该壁纸中的鱼不但可以游来游去,而且鱼的本身也是有动作的,含有动画的模型就是骨骼动画。本小节将介绍如何对有骨骼动画的MS3D文件类型进行加载,并存储其动画的相关数据,以及执行模型绘制的MS3DModel类。
(1)下面将详细介绍的是MS3DModel类的框架结构,这个类的主要作用是用于存储从ms3d文件中加载的动画相关数据,以及执行模型绘制。读者理解其代码框架,有助于更好地对本案例中鱼类的加载有更加深刻的理解。其代码框架如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/com/bn/ms3d/core/目录下的MS3DModel.java。
1 package com.bn.ms3d.core; //声明包名
2 ......//此处省略部分类和包的引入代码,读者可自行查阅光盘中的源代码
3 public class MS3DModel{
4 public FloatBuffer[] vertexCoordingBuffer; //顶点坐标数据缓冲
5 public FloatBuffer[] texCoordingBuffer; //纹理坐标数据缓冲
6 public FloatBuffer[] normalCoordingBuffer; //顶点法向量缓冲
7 public TextureManager textureManager; //纹理管理器
8 public MS3DHeader header; //头信息
9 public MS3DVertex[] vertexs; //顶点信息
10 public MS3DTriangle[] triangles; //三角形索引
11 public MS3DGroup[] groups; //组信息
12 public MS3DMaterial[] materials; //材质信息(纹理)
13 public float fps; //fps信息
14 public float current_time; //当前时间
15 public float totalTime; //总时间
16 public float frame_count; //关键帧数
17 public MS3DJoint[] joints; //关节信息
18 ......//此处省略了本类中部分成员变量的声明,读者可自行查看随书光盘中的源代码
19 private MS3DModel(MySurfaceView mv){
20 initShader(mv); //初始化着色器
21 }
22 ......//该处省略了与上节类似的initShader()方法,读者可自行查阅随书光盘中的源代码
23 public final void animate(float time,int texid){ //进行动画的方法
24 if(this.current_time != time){ //相同时间不做更新
25 this.updateJoint(time); //更新关节
26 this.updateVectexs(); //更新顶点
27 this.draw(true,texid); //执行绘制
28 }else{
29 this.draw(false,texid); //执行绘制
30 }}
31 public void updateJoint(float time){ //更新关节的方法
32 this.current_time = time; //更新当前时间
33 if(this.current_time > this.totalTime){ this.current_time = ; }
//时间超过总时间置为零
34 int size = this.joints.length; //获取关节数量
35 for(int i=0; i<size; i++){ //更新每个关节
36 this.joints[i].update(this.current_time);
37 }}
38 public void draw(boolean isUpdate,int texid){ //绘制模型
39 ......//该处省略了部分代码,将在后面的步骤中给出
40 }
41 private void updateVectexs(){ //动画中更新顶点数据
42 ......//该处省略了部分代码,将在后面的步骤中给出
43 }
44 private void updateVectex(int index){ //更新特定顶点的方法
45 ......//该处省略了部分代码,将在后面的步骤中给出
46 }
47 public final static MS3DModel load(InputStream is,
48 TextureManager manager,MySurfaceView mv){
49 ......//该处省略了部分代码,将在后面的步骤中给出
50 }
51 private void initBuffer(){ //初始化缓冲
52 ......//该处省略了部分代码,读者可自行查看随阅随书光盘中的源代码
53 }
54 ......//该处省略了获取动画总时间的getTotalTime方法,读者可自行查阅随书光盘中的源代码
55 }
第4~18行定义了一些本类中需要的一些成员变量,其中最为重要的是第8~17行的成员变量,这些成员变量分别对应于ms3d文件头信息、顶点信息、三角形索引、组信息等重要数据,读者了解后有助于更好地理解本类。
第23~30行为更新关节、顶点数据到动画中指定时刻及绘制模型的animate方法,若需要更新到的动画时刻与当前不同,则首先需要更新关节数据,再更新顶点数据,再执行模型的绘制,否则直接绘制模型。
第31~37行为更新关节数据的updateJoint方法,首先判断新的当前时间是否大于总的动画时间,若大于总的动画时间则将其设置为0,表示一轮动画播放完毕后从头开始。接着对每个关节进行遍历,调用每个关节信息对象的update方法更新每个关节。
第38~50行为省略掉的一些重要的功能方法,将在后面的步骤中详细介绍。
(2)下面介绍的是步骤(1)中省略的MS3DModel类中用于绘制模型的draw方法,此方法是将加载ms3d文件中的各种数据进行组装送入缓冲区,然后进行绘制的重要功能方法,读者理解其功能,有助于读者更好地理解本类。其具体代码如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/com/bn/ms3d/core/目录下的MS3DModel.java。
1 public void draw(boolean isUpdate,int texid){ //绘制模型
2 GLES20.glUseProgram(mProgram); //指定使用某套shader程序
3 MatrixState.copyMVMatrix(); //调用copyMVMatrix()方法复制矩阵
4 //将最终变换矩阵传入shader程序
5 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinal
Matrix(), 0);
//将变换矩阵传入shader程序
6 GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getM
Matrix(), 0);
7 //将光源位置传入着色器程序
8 GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
9 //将摄像机位置传入着色器程序
10 GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
11 int group_size = this.groups.length; //获取组的数量
12 MS3DTriangle triangle = null; //获取处理三角形信息对象引用
13 MS3DGroup group = null; //当前组信息引用
14 int[] indexs = null; //三角形的索引数组
15 int[] vertexIndexs = null; //顶点索引
16 FloatBuffer buffer = null; //buffer缓冲
17 MS3DMaterial material = null; //材质
18 for(int i=0; i<group_size; i++){
19 group = this.groups[i]; //获取当前组信息对象
20 indexs = group.getIndicies(); //获取组内三角形的索引数组
21 int triangleCount = indexs.length; //获取组内三角形的数量
22 if(group.getMaterialIndex() > -1){ //有材质(需要贴纹理)
23 material = this.materials[group.getMaterialIndex()];
24 this.textureManager.fillTexture(material.getName());
25 GLES20.glVertexAttribPointer(maTexCoorHandle,//将纹理坐标缓冲送入渲染管线
26 2, GLES20.GL_FLOAT,false, 2*4,this.texCoordingBuffer[i]);
27 GLES20.glEnableVertexAttribArray(maTexCoorHandle);//启用纹理坐标数组
28 }
29 if(isUpdate){//更新顶点缓冲
30 buffer = this.vertexCoordingBuffer[i];
31 for(int j=0; j<triangleCount; j++){ //对组内的每个三角形循环
32 triangle = this.triangles[indexs[j]];//获取当前要处理三角形信息对象
33 vertexIndexs = triangle.getIndexs();//获取三角形中3个顶点的顶点索引
34 for(int k=0; k<3; k++){ //完成三角形的组装
35 buffer.put(this.vertexs[vertexIndexs[k]].getCurr
Position().getVector3fArray());
36 }}
37 buffer.position(0);
38 }
39 GLES20.glVertexAttribPointer(maPositionHandle,//将顶点坐标缓冲送入渲染管线
40 3, GLES20.GL_FLOAT,false,3*4,this.vertexCoordingBuffer[i]);
41 GLES20.glVertexAttribPointer(maNormalHandle,//将顶点法向量数据传入渲染管线
42 3,GLES20.GL_FLOAT,false, 3*4, this.normalCoordingBuffer[i]);
43 GLES20.glEnableVertexAttribArray(maPositionHandle); //启用顶点坐标数组
44 GLES20.glEnableVertexAttribArray(maNormalHandle); //启用顶点法向量数组
45 GLES20.glActiveTexture(GLES20.GL_TEXTURE1); //绑定纹理
46 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texid);
47 GLES20.glUniform1i(BenWl, 1); //将明暗采样纹理传入片元着色器
48 //用顶点法进行绘制
49 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, triangleCount * 3);
50 }}
第2~10行是首先指定某套shader程序,然后将矩阵复制。本案例中因为给鱼类加上了灯光,所以就需要把总变换矩阵、变换矩阵、摄像机位置、灯光位置传入以进行操作。这样就可以在着色器中根据灯光位置、摄像机位置来给鱼类添加灯光特效。
第18~28行是对纹理坐标数据的操作,用一轮遍历来遍历获取当前组信息对象、组内三角形的索引数组及组内三角形的数量。并根据一个标志位来判断是否有材质,来断定是否需要纹理贴图,然后将纹理坐标缓冲送入渲染管线,启用纹理坐标数组。
第29~39行是动态的计算有动画的模型的顶点数据,所以在顶点数据有更新的情况下,根据三角形组装信息(组成三角形的3个顶点的索引)重新填充模型顶点坐标数据缓冲,以便绘制出最新姿态的模型,并将顶点坐标缓冲送入渲染管线。
第41~49行首先将顶点法向量数据送入渲染管线,然后开启顶点坐标数据、顶点法向量数组,并将纹理绑定。因为本案例中鱼类本身有逼真的深水明暗条纹,所以需要将明暗采样纹理传入着色器以进行操作,最后用定点法进行绘制。
(3)接下来介绍的是MS3DModel类中省略的用于更新顶点数据的updateVectexs方法,以及用于更新指定索引顶点数据updateVectex方法,具体代码如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/com/bn/ms3d/core/目录下的MS3DModel.java。
1 private void updateVectexs(){ //动画中更新顶点数据的方法
2 int count = this.vertexs.length; //获取顶点数量
3 for(int i=0; i<count; i++){ //更新每个顶点
4 this.updateVectex(i); //更新顶点
5 }}
6 private void updateVectex(int index){ //更新特定顶点的方法
7 MS3DVertex vertex = this.vertexs[index]; //获取顶点信息对象
8 if(vertex.getBone() == -1){ //若无关节控制
9 vertex.setCurrPosition(vertex.getInitPosition()); //更新顶点数据
10 }
11 else{ //若有关节控制
12 MS3DJoint joint = this.joints[vertex.getBone()]; //获取对应的关节
13 //根据关节的实时变换情况计算出顶点经关节影响后的位置
14 vertex.setCurrPosition(joint.getMatrix().transform(joint.
15 getAbsolute().invTransformAndRotate(vertex.getInitPosition())));
16 }}
第1~5行为更新顶点数据的updateVectexs方法,其中对每一个顶点进行遍历,调用updateVectex方法更新每一个顶点的数据。
第6~16行为更新指定索引顶点数据的updateVectex方法,其中对有关关节控制的顶点根据关节的实时变换情况计算出定点经关节影响后的位置。
(4)接下来介绍步骤(1)中的用于从指定ms3d模型文件的输入流中加载ms3d模型的load方法。该方法并不复杂,它按照ms3d文件数据组织格式依次加载了各部分的数据。读者理解这部分有助于提高对ms3d文件的认知。其具体代码如下。
代码位置:见随书光盘源代码/第2章/MyWallPaper/src/com/bn/ms3d/core/目录下的MS3DModel.java
1 //加载模型的方法
2 public final static MS3DModel load(InputStream is, TextureManager manager,
MySurfaceView mv){
3 MS3DModel model = null; //ms3d模型对象引用
4 SmallEndianInputStream fis = null; //特殊输入流对象引用
5 try{
6 fis = new SmallEndianInputStream(is); //创建特殊输入流对象
7 model = new MS3DModel(mv); //创建ms3d模型对象
8 model.textureManager = manager; //纹理管理器
9 model.header = MS3DHeader.load(fis); //加载头信息
10 model.vertexs = MS3DVertex.load(fis); //加载顶点信息
11 model.triangles = MS3DTriangle.load(fis); //加载三角形组装信息
12 model.groups = MS3DGroup.load(fis); //加载组信息
13 model.materials = MS3DMaterial.load(fis, manager); //加载材质信息
14 model.fps = fis.readFloat(); //加载帧速率信息
15 model.current_time = fis.readFloat(); //当前时间
16 model.frame_count = fis.readInt(); //关键帧数
17 model.totalTime = model.frame_count / model.fps; //计算动画总时间
18 model.joints = MS3DJoint.load(fis); //加载关节信息
19 model.initBuffer(); //初始化缓冲
20 }catch (IOException e){
21 e.printStackTrace(); //打印异常
22 }
23 finally{
24 if(fis != null){ //若输入流不为空
25 try {
26 fis.close(); //关闭输入流
27 }catch (IOException e){ //异常处理
28 e.printStackTrace(); //打印异常信息
29 }}}
30 System.gc(); //申请垃圾回收
31 return model; //返回加载的模型对象
32 }
第3~4行首先拿到ms3d模型的引用,然后再拿到特殊输入流对象SmallEndianInputStream的引用,为后面的模型导入做准备。
第6~32行是按照ms3d文件数据组织格式依次加载了各部分的数据,依次加载的数据为文件头、顶点、三角形组装、组、材质、帧速率、当前播放时间,关键帧数量关节信息,进行异常检查,保证加载的正确性,并返回模型。
说明
本节中用于从输入流加载ms3d模型的load方法并不复杂,它按照ms3d文件格式依次加载各部分的数据。各部分数据的具体加载并不是在此方法中实现的,而是在各部分数据对应的类中实现的,读者可查看随书光盘中的源代码。另外,此方法中包含的一个输入流类——SmallEndianInputStream,并不复杂,读者可查看随书光盘中的源代码。