《Android 应用案例开发大全(第3版)》——第2章,第2.5节辅助绘制类

简介:

本节书摘来自异步社区《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,并不复杂,读者可查看随书光盘中的源代码。

相关文章
|
2天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
22 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
20天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
21天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
1天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
4 0
Android系统 应用存储路径与权限
|
6天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
3 0
|
6天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
6 0
|
7天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
9天前
|
搜索推荐 开发工具 Android开发
安卓即时应用(Instant Apps)开发指南
【4月更文挑战第14天】Android Instant Apps让用户体验部分应用功能而无需完整下载。开发者需将应用拆分成模块,基于已上线的基础应用构建。使用Android Studio的Instant Apps Feature Library定义模块特性,优化代码与资源以减小模块大小,同步管理即时应用和基础应用的版本。经过测试,可发布至Google Play Console,提升用户便利性,创造新获客机会。
|
10天前
|
Java API 调度
安卓多线程和并发处理:提高应用效率
【4月更文挑战第13天】本文探讨了安卓应用中多线程和并发处理的优化方法,包括使用Thread、AsyncTask、Loader、IntentService、JobScheduler、WorkManager以及线程池。此外,还介绍了RxJava和Kotlin协程作为异步编程工具。理解并恰当运用这些技术能提升应用效率,避免UI卡顿,确保良好用户体验。随着安卓技术发展,更高级的异步处理工具将助力开发者构建高性能应用。
|
10天前
|
编解码 人工智能 测试技术
安卓适配性策略:确保应用在不同设备上的兼容性
【4月更文挑战第13天】本文探讨了提升安卓应用兼容性的策略,包括理解平台碎片化、设计响应式UI(使用dp单位,考虑横竖屏)、利用Android SDK的兼容工具(支持库、资源限定符)、编写兼容性代码(运行时权限、设备特性检查)以及优化性能以适应低端设备。适配性是安卓开发的关键,通过这些方法可确保应用在多样化设备上提供一致体验。未来,自动化测试和AI将助力应对设备碎片化挑战。