利用JPCT-AE在安卓设备上进行*.obj文件的加载和显示

最近做一个项目,需要加载带纹理的3d模型obj文件并显示出来,仅限于安卓平台,因此需要一个轻量级的3d内核。

找了很多内核,要不就是太久没更新不能用了,要不就是功能太复杂,后来找到了JPCT-AE,网上的文档相当少,但经过自己研究了两天,终于成功完成了obj模型的加载和显示,有需要的同学可以参考借鉴。

1、JPCT-AE简介

官网:http://www.jpct.net/jpct-ae/index.html

支持加载3DS, OBJ, MD2, ASC,序列化文件,也支持mtl和纹理,基于egl实现。

注意:纹理只支持图片的宽高为2的n次方,如1024,512,如果纹理不是这些尺寸,那么在加载时会出错。此问题也有解决办法,解决办法详见后面的说明。


jpct-ae显示3d的流程大致如下:

     mWorld = new World();   //创建3d场景

     mWorld.setAmbientLight(20, 20, 20);  //添加环境光源

     mSun = new Light(mWorld);   //添加点光源

     mSun.setIntensity(250, 250, 250);   //设置点光源强度

     //此处略,利用loader加载3d模型

     // ......

     //加载3d模型完毕

    Camera cam = mWorld.getCamera();         //获取相机
    cam.moveCamera(Camera.CAMERA_MOVEOUT, 130);   //调整相机位置
    cam.lookAt(mObj.getTransformedCenter());       //调整相机视角


   场景绘制在一个GLSurfaceView上, 构建完毕后,在onDrawFrame方法中对场景进行渲染:

     mWorld.renderScene(mFb);
     mWorld.draw(mFb);


   在官方提供的demo中有很完整的示例代码,可以参考demo来完成代码的编写。


2、利用JPCT-AE加载obj模型

JPCT-AE提供一个静态的Loader类用于加载不同数据格式的3d模型,在官方文档中可以查到这个类的用法及api,加载.obj格式使用下面的api:

loadOBJ

public static Object3D[] loadOBJ(java.io.InputStream objStream,
                                 java.io.InputStream mtlStream,
                                 float scale)
此api会加载模型中的所有多边形的几何信息,支持多边形最多4个顶点(超过会有警告并且显示异常)。对于模型的texture文件,此api会加载成空文件,并不会实际读取,因此需要在执行此函数前预先将相关的texture文件进行加载,或者在此函数执行后对相关的texture文件进行替换,否则texture将不能显示。

同时,JPCT-AE提供一个类TextureManager用于管理纹理,此类是一个单例,用于存储和取出textures,常用的有addTexture, replaceTexture, containsTexture, unloadTexture,用法很简单,详细说明可以查官方api文档。

整理以上思路,得知正确的显示出带纹理的obj模型,需要做以下几步:

1)解析obj文件,得到mtl文件;

2)解析mtl文件,得到texture文件;

3)使用TextureManager类,加载所有的texture文件;

4)创建场景并显示。

2.1 解析obj文件,得到mtl文件

打开obj文件查看描述信息,其中以“mtllib”关键字开头,即是描述的mtl文件的名称,如:
mtllib male02.mtl

参考代码:
利用JPCT-AE在安卓设备上进行*.obj文件的加载和显示_第1张图片    

2.2  解析mtl文件,得到texture文件

打开mtl查看texture信息,其中以"newmtl"关键字开关,描述的就是纹理信息,一个mtl文件中可以描述多个纹理,如:

newmtl _01_-_Default1noCulli__01_-_Default1noCulli
Ns 154.901961
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.165000 0.165000 0.165000
Ni 1.000000
d 1.000000
illum 2
map_Kd 01_-_Default1noCulling.JPG

newmtl FrontColorNoCullingID_male-02-1noCulling.JP
Ns 154.901961
Ka 0.000000 0.000000 0.000000
Kd 0.800000 0.800000 0.800000
Ks 0.165000 0.165000 0.165000
Ni 1.000000
d 1.000000
illum 2
map_Kd male-02-1noCulling.JPG

map_Kd 表示材质的固有色,map_Ka表示材质的阴影色。

得到所有的纹理。参考代码:

利用JPCT-AE在安卓设备上进行*.obj文件的加载和显示_第2张图片

2.3 加载所有texture,参考代码如下:

利用JPCT-AE在安卓设备上进行*.obj文件的加载和显示_第3张图片


2.4 创建场景并显示

在onSurfaceCreated中创建场景,在onDrawFrame中进行绘制:

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    Log.i(TAG, "onSurfaceCreated enter!!");
    createWorld();
}

public void onDrawFrame(GL10 gl) {
    //Log.i(TAG, "onDrawFrame enter!!");
    // Draw the main screen
    mFb.clear(mBgColor);

    if(mWorld != null) {

        mWorld.renderScene(mFb);

        mWorld.draw(mFb);
        mFb.display();
    }
}

3、解决texture只支持宽高为2的n次幂的图片的问题。

解决办法是改变bitmap的大小,不过我这个解决办法很牵强,因为改变bitmap后会有一定程度的失真,另外也不是百分百适用,只适用于texture为正方形的情况。当然一般像3dmax这样的建模软件生成的texture肯定都是符合要求的。但是由于openge本身并没有对texture有宽高上的要求,所以jpct-ae有这样的限制感觉很不爽。

参考代码如下:

//jpct-ae only supports images size of which is 2^x 
public Bitmap scaleBitmap(Bitmap bitmap){  
            int w = bitmap.getWidth();  
            int h = bitmap.getHeight();
            int destW = 1024;
            int destH = h*destW/w;
     
            Bitmap newbm = Bitmap.createScaledBitmap(bitmap, destW, destH, true);  
            return newbm;  
    }


   总之,暂时利用jpct-ae快速解决了3d模型加载的问题,但还是觉得jpct-ae稍微有点重,而且存在一些限制不太方便,有空了还是自己利用egl写一个比较好。




你可能感兴趣的:(3D)