obj 文件是三维模型中一种经典的通用的文件格式,他记录下了三维模型中的大部分必要信息,下面将尝试在 unity 中将 obj 解析成 unity 的 mesh 网格。

了解obj文件结构

既然要解析obj文件,那么在这之前我们当然要对obj文件的结构有所了解,下面我们就了解一下一个 unity 中经典的 cube 的 obj 格式会是怎样。

o Cube // 网格名称
v 0.500000 -0.500000 0.500000  // 顶点数据 : v 字后面跟的就是顶点的局部坐标 x,y,z 的值
v -0.500000 -0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 -0.500000 0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000

vn 0.000000 0.000000 1.000000  // 法线数据
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000

vt 0.000000 0.000000  // UV数据
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000

g Default-Material  // 材质名称
usemtl Default-Material  // 使用的材质
usemap Default-Material  // 使用的贴图

f 1/1/1 3/3/3 4/4/4  // 三角形数据 - 顶点索引/UV索引/法线索引
f 1/1/1 4/4/4 2/2/2
f 9/9/9 5/5/5 6/6/6
f 9/9/9 6/6/6 10/10/10
f 11/11/11 7/7/7 8/8/8
f 11/11/11 8/8/8 12/12/12
f 13/13/13 14/14/14 15/15/15
f 13/13/13 15/15/15 16/16/16
f 17/17/17 18/18/18 19/19/19
f 17/17/17 19/19/19 20/20/20
f 21/21/21 22/22/22 23/23/23
f 21/21/21 23/23/23 24/24/24

解析成mesh

了解了obj文件的结构就会发现,解析它其实挺简单的,只需要根据每一行的前缀确定后面的数据到底属于哪一种类型,然后将它分割保存起来,最后赋值给mesh中的对应属性即可。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class ObjParser{

    public static Mesh Parse(string url)
    {
        Mesh mesh = new Mesh();

        List<Vector3> vts = new List<Vector3>();
        List<Vector2> uvs = new List<Vector2>();
        List<Vector3> normals = new List<Vector3>();
        List<int> tris = new List<int>();


        StreamReader sr = new StreamReader(url);

        while(!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            string[] split = line.Split(' ');

            switch(split[0])
            {
                case "o":
                    mesh.name = split[1];
                    break;

                case "v":
                    {
                        float x = float.Parse(split[1]);
                        float y = float.Parse(split[2]);
                        float z = float.Parse(split[3]);

                        vts.Add(new Vector3(x, y, z));
                    }
                    break;
                case "vn":
                    {
                        float x = float.Parse(split[1]);
                        float y = float.Parse(split[2]);
                        float z = float.Parse(split[3]);

                        normals.Add(new Vector3(x, y, z));
                    }
                    break;

                case "vt":
                    {
                        float x = float.Parse(split[1]);
                        float y = float.Parse(split[2]);

                        uvs.Add(new Vector2(x, y));
                    }
                    break;

                case "f":
                    {
                        for (int i = 1; i < split.Length; ++i)
                        {
                            string[] sp = split[i].Split('/');
                            tris.Add(int.Parse(sp[0]) - 1);
                        }
                    }
                    break;

                default:

                    break;
            }
        }

        mesh.vertices = vts.ToArray();
        mesh.uv = uvs.ToArray();
        mesh.normals = normals.ToArray();
        mesh.triangles = tris.ToArray();

        return mesh;
    }

}