• Obj格式解析以及在Unity3D下导入测试


    目前基本实现了导入,注意只能打开含有单个模型的obj文件

    四边面模型:

    全三角面模型(测试单一材质,自动分了下UV):

    这里介绍下obj格式:

    obj格式是waveFront推出的一种3D模型格式,可以存放静态模型以及一些诸如曲线的附加信息。

    其格式以文本形式存放,所以解析起来比较方便,它的大体格式如下:

    # WaveFront *.obj file (generated by CINEMA 4D)
    
    mtllib ./test.mtl
    
    v -100.00000000000000 -100.00000000000000 -100.00000000000000
    v -100.00000000000000 100.00000000000000 -100.00000000000000
    v 100.00000000000000 -100.00000000000000 -100.00000000000000
    v 100.00000000000000 100.00000000000000 -100.00000000000000
    v 100.00000000000000 -100.00000000000000 100.00000000000000
    v 100.00000000000000 100.00000000000000 100.00000000000000
    v -100.00000000000000 -100.00000000000000 100.00000000000000
    v -100.00000000000000 100.00000000000000 100.00000000000000
    # 8 vertices
    
    vn 0.00000000000000 0.00000000000000 -1.00000000000000
    vn 1.00000000000000 0.00000000000000 0.00000000000000
    vn 0.00000000000000 0.00000000000000 1.00000000000000
    vn -1.00000000000000 0.00000000000000 0.00000000000000
    vn 0.00000000000000 1.00000000000000 0.00000000000000
    vn 0.00000000000000 -1.00000000000000 0.00000000000000
    # 6 normals
    
    vt 0.00000000000000 -1.00000000000000 0.00000000000000
    vt 0.00000000000000 -0.00000000000000 0.00000000000000
    vt 1.00000000000000 -0.00000000000000 0.00000000000000
    vt 1.00000000000000 -1.00000000000000 0.00000000000000
    # 4 texture coordinates
    
    o Cube
    usemtl default
    f 1/4/1 2/3/1 4/2/1 3/1/1
    f 3/4/2 4/3/2 6/2/2 5/1/2
    f 5/4/3 6/3/3 8/2/3 7/1/3
    f 7/4/4 8/3/4 2/2/4 1/1/4
    f 2/4/5 8/3/5 6/2/5 4/1/5
    f 7/4/6 1/3/6 3/2/6 5/1/6
    Cube(Obj)

    常用类型:

    #开头表示注释

    v表示顶点

    vn表示法线,可以共用法线

    vt表示uv坐标

    f表示一个面,比如参数1/4/1,表示顶点索引/UV索引/法线索引

    下面贴一下工具类的代码:

    using System;
    using System.Linq;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace Hont
    {
        public class ObjFormatAnalyzer
        {
            public struct Vector
            {
                public float X;
                public float Y;
                public float Z;
            }
    
            public struct FacePoint
            {
                public int VertexIndex;
                public int TextureIndex;
                public int NormalIndex;
            }
    
            public struct Face
            {
                public FacePoint[] Points;
                public bool IsQuad;
            }
    
            public Vector[] VertexArr;
            public Vector[] VertexNormalArr;
            public Vector[] VertexTextureArr;
            public Face[] FaceArr;
    
    
            public void Analyze(string content)
            {
                content = content.Replace('
    ', ' ').Replace('	', ' ');
    
                var lines = content.Split('
    ');
                var vertexList = new List<Vector>();
                var vertexNormalList = new List<Vector>();
                var vertexTextureList = new List<Vector>();
                var faceList = new List<Face>();
    
                for (int i = 0; i < lines.Length; i++)
                {
                    var currentLine = lines[i];
    
                    if (currentLine.Contains("#") || currentLine.Length == 0)
                    {
                        continue;
                    }
    
                    if (currentLine.Contains("v "))
                    {
                        var splitInfo = currentLine.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        vertexList.Add(new Vector() { X = float.Parse(splitInfo[1]), Y = float.Parse(splitInfo[2]), Z = float.Parse(splitInfo[3]) });
                    }
                    else if (currentLine.Contains("vt "))
                    {
                        var splitInfo = currentLine.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        vertexTextureList.Add(new Vector() { X = splitInfo.Length > 1 ? float.Parse(splitInfo[1]) : 0, Y = splitInfo.Length > 2 ? float.Parse(splitInfo[2]) : 0, Z = splitInfo.Length > 3 ? float.Parse(splitInfo[3]) : 0 });
                    }
                    else if (currentLine.Contains("vn "))
                    {
                        var splitInfo = currentLine.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        vertexNormalList.Add(new Vector() { X = float.Parse(splitInfo[1]), Y = float.Parse(splitInfo[2]), Z = float.Parse(splitInfo[3]) });
                    }
                    else if (currentLine.Contains("f "))
                    {
                        Func<string, int> tryParse = (inArg) =>
                         {
                             var outValue = -1;
                             return int.TryParse(inArg, out outValue) ? outValue : 0;
                         };
    
                        var splitInfo = currentLine.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        var isQuad = splitInfo.Length > 4;
                        var face1 = splitInfo[1].Split('/');
                        var face2 = splitInfo[2].Split('/');
                        var face3 = splitInfo[3].Split('/');
                        var face4 = isQuad ? splitInfo[4].Split('/') : null;
                        var face = new Face();
                        face.Points = new FacePoint[4];
                        face.Points[0] = new FacePoint() { VertexIndex = tryParse(face1[0]), TextureIndex = tryParse(face1[1]), NormalIndex = tryParse(face1[2]) };
                        face.Points[1] = new FacePoint() { VertexIndex = tryParse(face2[0]), TextureIndex = tryParse(face2[1]), NormalIndex = tryParse(face2[2]) };
                        face.Points[2] = new FacePoint() { VertexIndex = tryParse(face3[0]), TextureIndex = tryParse(face3[1]), NormalIndex = tryParse(face3[2]) };
                        face.Points[3] = isQuad ? new FacePoint() { VertexIndex = tryParse(face4[0]), TextureIndex = tryParse(face4[1]), NormalIndex = tryParse(face4[2]) } : default(FacePoint);
                        face.IsQuad = isQuad;
    
                        faceList.Add(face);
                    }
                }
    
                VertexArr = vertexList.ToArray();
                VertexNormalArr = vertexNormalList.ToArray();
                VertexTextureArr = vertexTextureList.ToArray();
                FaceArr = faceList.ToArray();
            }
        }
    }
    ObjFormatAnalyzer

    工厂类,目前只有输出GameObject:

    using UnityEngine;
    using System.IO;
    using System.Linq;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace Hont
    {
        public static class ObjFormatAnalyzerFactory
        {
            public static GameObject AnalyzeToGameObject(string objFilePath)
            {
                if (!File.Exists(objFilePath)) return null;
    
                var objFormatAnalyzer = new ObjFormatAnalyzer();
    
                objFormatAnalyzer.Analyze(File.ReadAllText(objFilePath));
    
                var go = new GameObject();
                var meshRenderer = go.AddComponent<MeshRenderer>();
                var meshFilter = go.AddComponent<MeshFilter>();
    
                var mesh = new Mesh();
    
                var sourceVertexArr = objFormatAnalyzer.VertexArr;
                var sourceUVArr = objFormatAnalyzer.VertexTextureArr;
                var faceArr = objFormatAnalyzer.FaceArr;
                var notQuadFaceArr = objFormatAnalyzer.FaceArr.Where(m => !m.IsQuad).ToArray();
                var quadFaceArr = objFormatAnalyzer.FaceArr.Where(m => m.IsQuad).ToArray();
                var vertexList = new List<Vector3>();
                var uvList = new List<Vector2>();
    
                var triangles = new int[notQuadFaceArr.Length * 3 + quadFaceArr.Length * 6];
                for (int i = 0, j = 0; i < faceArr.Length; i++)
                {
                    var currentFace = faceArr[i];
    
                    triangles[j] = j;
                    triangles[j + 1] = j + 1;
                    triangles[j + 2] = j + 2;
    
                    var vec = sourceVertexArr[currentFace.Points[0].VertexIndex - 1];
                    vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                    var uv = sourceUVArr[currentFace.Points[0].TextureIndex - 1];
                    uvList.Add(new Vector2(uv.X, uv.Y));
    
                    vec = sourceVertexArr[currentFace.Points[1].VertexIndex - 1];
                    vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                    uv = sourceUVArr[currentFace.Points[1].TextureIndex - 1];
                    uvList.Add(new Vector2(uv.X, uv.Y));
    
                    vec = sourceVertexArr[currentFace.Points[2].VertexIndex - 1];
                    vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                    uv = sourceUVArr[currentFace.Points[2].TextureIndex - 1];
                    uvList.Add(new Vector2(uv.X, uv.Y));
    
                    if (currentFace.IsQuad)
                    {
                        triangles[j + 3] = j + 3;
                        triangles[j + 4] = j + 4;
                        triangles[j + 5] = j + 5;
                        j += 3;
    
                        vec = sourceVertexArr[currentFace.Points[0].VertexIndex - 1];
                        vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                        uv = sourceUVArr[currentFace.Points[0].TextureIndex - 1];
                        uvList.Add(new Vector2(uv.X, uv.Y));
    
                        vec = sourceVertexArr[currentFace.Points[2].VertexIndex - 1];
                        vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                        uv = sourceUVArr[currentFace.Points[2].TextureIndex - 1];
                        uvList.Add(new Vector2(uv.X, uv.Y));
    
                        vec = sourceVertexArr[currentFace.Points[3].VertexIndex - 1];
                        vertexList.Add(new Vector3(vec.X, vec.Y, vec.Z));
    
                        uv = sourceUVArr[currentFace.Points[3].TextureIndex - 1];
                        uvList.Add(new Vector2(uv.X, uv.Y));
                    }
    
                    j += 3;
                }
    
                mesh.vertices = vertexList.ToArray();
                mesh.uv = uvList.ToArray();
                mesh.triangles = triangles;
    
                meshFilter.mesh = mesh;
                meshRenderer.material = new Material(Shader.Find("Standard"));
    
                return go;
            }
        }
    }
    ObjFormatAnalyzerFactory

    测试使用脚本:

    public class ObjFormatAnalyzerTest : MonoBehaviour
    {
        void Start()
        {
            var go = ObjFormatAnalyzerFactory.AnalyzeToGameObject(@"D:TestObjcentaur.obj");
    
            WWW www = new WWW("file:/D:/TestObj/texture.jpg");
    
            while (!www.isDone) { }
    
            go.GetComponent<MeshRenderer>().material.mainTexture = www.texture;
        }
    }

    然后挂载运行即可。

    目前可能还有点小问题,因为没有实现材质的绑定,所以只能支持单一材质。

  • 相关阅读:
    动词 + to do、动词 + doing
    图像直线检测——霍夫线变换
    x=min(x, y)
    x=min(x, y)
    算法 Tricks(三)—— 数组(序列)任意区间最小(大)值
    算法 Tricks(三)—— 数组(序列)任意区间最小(大)值
    分治法求解切割篱笆
    分治法求解切割篱笆
    GMM的EM算法实现
    秒杀多线程第二篇 多线程第一次亲热接触 CreateThread与_beginthreadex本质差别
  • 原文地址:https://www.cnblogs.com/hont/p/5239725.html
Copyright © 2020-2023  润新知