opengl es 2.0中加载.obj 与 .mtl(上篇)

达芬奇密码2018-07-18 13:25

项目中有个load obj需求,在网上看了很多的例子,也找了几个开源框架,发现其在解析obj与mtl文件上均不完善(其中包括2012年我们几个写的《Android 3D游戏开发技术宝典——OpenGL ES 2.0》)。 比如:

  • mind3d 2011年就已停止维护(为opengl es1.0),并且在加载多图形上存在很大的不兼容。
  • 《Android 3D游戏开发技术宝典——OpenGL ES 2.0》 第九章 3D模型加载。 2012年我们几个在写这本书时,只是简单解析了obj文件,而且对mtl文件并未做解析(看到网上很多的例子是把这一章的案例直接照搬了)

mind3d官方地址与源码https://code.google.com/archive/p/min3d/ https://github.com/deadmoose/min3d

《Android 3D游戏开发技术宝典——OpenGL ES 2.0》请自行京东

我觉得,项目开发中时间紧张,不应该把时间话费在obj与mtl文件解析上。 所以,打算搞一个兼容性较强obj 3d文件加载库,便于以后项目中再次遇到时,可节省大量项目时间。

效果图




.obj 和 .mtl文件格式

对于obj与mtl文件格式不太了解的同学,可参考: http://blog.csdn.net/xiaxl/article/details/76893165

mtl文件解析代码

/**
 * @author xiaxl
 *         <p>
 *         create by xiaxl on 2017.08.09
 *         加载材质工具类
 */
public class MtlLoaderUtil {

    private static final String TAG = "MtlLoaderUtil";


    /**
     * 加载材质的方法
     *
     * @param fname assets的mtl文件路径
     * @param res
     * @return
     */
    public static HashMap<String, MtlData> load(String fname, Resources res) throws Exception {
        // 材质数组
        HashMap<String, MtlData> mMTLMap = new HashMap<String, MtlData>();
        //
        if (res == null || TextUtils.isEmpty(fname)) {
            return mMTLMap;
        }
        //
        MtlData currMtlData = null;
        try {
            // 读取assets下文件
            InputStream in = res.getAssets().open(fname);
            InputStreamReader isr = new InputStreamReader(in);
            BufferedReader buffer = new BufferedReader(isr);
            // 行数据
            String line;
            //
            while ((line = buffer.readLine()) != null) {
                // Skip comments and empty lines.
                if (line.length() == 0 || line.charAt(0) == '#') {
                    continue;
                }
                //
                StringTokenizer parts = new StringTokenizer(line, " ");
                int numTokens = parts.countTokens();
                if (numTokens == 0) {
                    continue;
                }
                //
                String type = parts.nextToken();
                type = type.replaceAll("\\t", "");
                type = type.replaceAll(" ", "");

                // 定义一个名为 'xxx'的材质
                if (type.equals(MtlLoaderUtil.NEWMTL)) {
                    String name = parts.hasMoreTokens() ? parts.nextToken() : "def";
                    // 将上一个对象加入到列表中
                    if (currMtlData != null) {
                        mMTLMap.put(currMtlData.name, currMtlData);
                    }
                    // 创建材质对象
                    currMtlData = new MtlData();
                    // 材质对象名称
                    currMtlData.name = name;
                }
                // 环境光
                else if (type.equals(MtlLoaderUtil.KA)) {
                    currMtlData.Ka_Color = getColorFromParts(parts);
                }
                // 散射光
                else if (type.equals(MtlLoaderUtil.KD)) {
                    currMtlData.Kd_Color = getColorFromParts(parts);
                }
                // 镜面光
                else if (type.equals(MtlLoaderUtil.KS)) {
                    currMtlData.Ks_Color = getColorFromParts(parts);
                }
                // 高光调整参数
                else if (type.equals(MtlLoaderUtil.NS)) {
                    String ns = parts.nextToken();
                    currMtlData.ns = Float.parseFloat(ns);
                }
                // 溶解度,为0时完全透明,1完全不透明
                //else if (type.equals(MtlLoaderUtil.D) || type.equals(MtlLoaderUtil.TR)) {
                else if (type.equals(MtlLoaderUtil.D)) {
                    currMtlData.alpha = Float.parseFloat(parts.nextToken());
                }
                // map_Ka,map_Kd,map_Ks:材质的环境(ambient),散射(diffuse)和镜面(specular)贴图
                else if (type.equals(MtlLoaderUtil.MAP_KA)) {
                    currMtlData.Ka_Texture = parts.nextToken();
                } else if (type.equals(MtlLoaderUtil.MAP_KD)) {
                    currMtlData.Kd_Texture = parts.nextToken();
                } else if (type.equals(MtlLoaderUtil.MAP_KS)) {
                    currMtlData.Ks_ColorTexture = parts.nextToken();
                } else if (type.equals(MtlLoaderUtil.MAP_NS)) {
                    currMtlData.Ns_Texture = parts.nextToken();
                } else if (type.equals(MtlLoaderUtil.MAP_D) || type.equals(MtlLoaderUtil.MAP_TR)) {
                    currMtlData.alphaTexture = parts.nextToken();
                } else if (type.equals(MtlLoaderUtil.MAP_BUMP)) {
                    currMtlData.bumpTexture = parts.nextToken();
                }
            }
            if (currMtlData != null) {
                mMTLMap.put(currMtlData.name, currMtlData);
            }
            buffer.close();
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            throw new Exception(e.getMessage(), e.getCause());
        }
        return mMTLMap;
    }

    //####################################################################################


    /**
     * 材质需解析字段
     */
    // 定义一个名为 'xxx'的材质
    private static final String NEWMTL = "newmtl";
    // 材质的环境光(ambient color)
    private static final String KA = "Ka";
    // 散射光(diffuse color)用Kd
    private static final String KD = "Kd";
    // 镜面光(specular color)用Ks
    private static final String KS = "Ks";
    // 反射指数 定义了反射高光度。该值越高则高光越密集,一般取值范围在0~1000。
    private static final String NS = "Ns";
    // 渐隐指数描述 参数factor表示物体融入背景的数量,取值范围为0.0~1.0,取值为1.0表示完全不透明,取值为0.0时表示完全透明。
    private static final String D = "d";
    // 滤光透射率
    private static final String TR = "Tr";
    // map_Ka,map_Kd,map_Ks:材质的环境(ambient),散射(diffuse)和镜面(specular)贴图
    private static final String MAP_KA = "map_Ka";
    private static final String MAP_KD = "map_Kd";
    private static final String MAP_KS = "map_Ks";
    private static final String MAP_NS = "map_Ns";
    private static final String MAP_D = "map_d";
    private static final String MAP_TR = "map_Tr";
    private static final String MAP_BUMP = "map_Bump";

    public static class MtlData {

        // 材质对象名称
        public String name;
        // 环境光
        public int Ka_Color;
        // 散射光
        public int Kd_Color;
        // 镜面光
        public int Ks_Color;
        // 高光调整参数
        public float ns;
        // 溶解度,为0时完全透明,1完全不透明
        public float alpha = 1f;
        // map_Ka,map_Kd,map_Ks:材质的环境(ambient),散射(diffuse)和镜面(specular)贴图
        public String Ka_Texture;
        public String Kd_Texture;
        public String Ks_ColorTexture;
        public String Ns_Texture;
        public String alphaTexture;
        public String bumpTexture;
    }


    //####################################################################################

    /**
     * 返回一个oxffffffff格式的颜色值
     *
     * @param parts
     * @return
     */
    private static int getColorFromParts(StringTokenizer parts) {
        int r = (int) (Float.parseFloat(parts.nextToken()) * 255f);
        int g = (int) (Float.parseFloat(parts.nextToken()) * 255f);
        int b = (int) (Float.parseFloat(parts.nextToken()) * 255f);
        return Color.rgb(r, g, b);
    }
}


相关阅读:

opengl es 2.0中加载.obj 与 .mtl(下篇)

本文来自网易实践者社区,经作者夏学良授权发布。