Unity 在场景中导入3D模型 && 切换场景保留模型

前言

  • 写这篇文章的初衷是为了记录做进做的一个工程中遇到的一个关于匹配字节的效率问题
    为了将.obj格式的文件读入到Unity中 其包含的步骤包含3个过程:
    1、读取obj文件
    2、将读到的值存储下来
    3、赋值给gameobject 并作渲染
  • 其中第一步耗时很少 对于一个12.2M(561606行数据)的文件,其读取时间仅仅为500ms(即0.5s),但是readalllines()函数读取到的字符串需要在赋值之前做一个处理 网上所查到的资料有几种处理方法:Indexof()、split()将字符串分割为多个子字符串。
  • 但是以上提及到的方法效率非常慢 c++有一个读取固定格式的方法,效率很高:fscanf()函数 读取并进行赋值 整个流程大概在15s以内,因此我想到在C#中重写fscanf()函数来提高读取文件的速度,这就需要用到正则表达式来进行字符匹配。
  • obj文件长什么样?
  • 在这里插入图片描述
    在这里插入图片描述

1.正则表达式

  • 基本语法
字符描述
\cx匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。
\f匹配一个换页符。等价于 \x0c 和 \cL。
\n匹配一个换行符。等价于 \x0a 和 \cJ。
\r匹配一个回车符。等价于 \x0d 和 \cM。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符。
\S匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t匹配一个制表符。等价于 \x09 和 \cI。
\v匹配一个垂直制表符。等价于 \x0b 和 \cK。
特殊字符描述
$匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n’ 或 ‘\r’。要匹配 $ 字符本身,请使用 $。
( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
*匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 *。
+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
.匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 . 。
[标记一个中括号表达式的开始。要匹配 [,请使用 [。
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 ?。
\将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\’ 匹配 “”,而 ‘(’ 则匹配 “(”。
^匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 ^。
{标记限定符表达式的开始。要匹配 {,请使用 {。
竖线(这里打不出来)指明两项之间的一个选择。要匹配

1.1 怎么写

  • 常用的正则匹配:
匹配项正则表达式
String[\w\d\S]+
Int16-[0-9]+
UInt16[0-9]+
Int32-[0-9]+
UInt32[0-9]+
Int64-[0-9]+
UInt64[0-9]+
Single[-]
Double[-]
Booleantrue
Byte[0-9]{1,3}
SByte-[0-9]{1,3}
Char[\w\S]{1}
Decimal[-]
  • 注意:
    正则表达式最好根据自己的需求进行写 以上仅作为参考
    比如:上面的Double的正则表达式无法匹配这样的数:328.501
    我自己的匹配正则表达式为:-?([0-9]{1,4}[.]?[0-9]{0,5})
    可以把自己写的正则表达式在这个网站进行测试:正则表达式在线测试工具

1.2 对应的代码

  • 选择读取的文件
     public void SelectFile()
    {
        OpenFileDialog ofd = new OpenFileDialog();
        ofd.Filter = "obj文件(*.obj)|*.obj";
        ofd.Title = "选择待手术的对象";
        ofd.InitialDirectory = "file://" + UnityEngine.Application.dataPath;//默认打开路径 
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            Debug.Log(ofd.FileName);
            // 读出文本文件的所有行
            int start_ReadAllLines = System.Environment.TickCount;
            string[] lines = File.ReadAllLines(ofd.FileName);
            Debug.Log("读取文件耗时:" + (System.Environment.TickCount-start_ReadAllLines));
            //string[] lines = File.ReadAllLines(ofd.InitialDirectory);//现在的InitialDirectory不适合使用
            GameObject o = OBJCreate(lines);
            o.GetComponent<Transform>().position =new Vector3(0.5f,-0.5f,-0.5f);
            if (o.AddComponent<Interactable>() != null)
                Debug.Log("Interactable has loaded");
            if (o.AddComponent<Choosable>() != null)
                Debug.Log("Choosable has loaded");
            //o.AddComponent<Interactable>();
            //o.AddComponent<Choosable>();
            //newone.GetComponent<Transform>().transform = new Transform(0, 0, 0);
        }
        //Debug.Log("已经打开");
    }
    
    • 创建物体 分解字符串
    static GameObject OBJCreate(string[] lines)
    {
        Debug.Log("OBJCreate begin~");
        GameObject o = new GameObject();
        //var meshCollider = o.AddComponent<MeshCollider>();
        //var meshFilter = o.AddComponent<MeshFilter>();
        o.AddComponent<MeshCollider>();
        o.AddComponent<MeshFilter>();
        o.GetComponent<MeshFilter>().mesh.Clear();
        o.AddComponent<MeshRenderer>();
        
        List<Vector3> vertexList = new List<Vector3>();
        List<int> TrianglesList = new List<int>();
        Scanf s = new Scanf();
        int start_Regex = System.Environment.TickCount;
        for (int i = 0; i < lines.Length; i++)
        {
            var currentLine = lines[i];
            MatchCollection matches;
            //if (currentLine.Contains("#") || currentLine.Length == 0)
            if (currentLine[0]=='#' || currentLine.Length == 0)
            {
                Debug.Log("#");
                continue;
            }
    
            if (currentLine[0] == 'v')
            {
                Debug.Log("V");
                matches = s.Scan(currentLine, 1);
                vertexList.Add(new Vector3((float)Double.Parse(matches[0].Value), (float)Double.Parse(matches[1].Value), (float)Double.Parse(matches[2].Value)));
                Debug.Log(vertexList[vertexList.Count - 1].x +" "+ vertexList[vertexList.Count - 1].y + " " + vertexList[vertexList.Count - 1].z);
               
            else if (currentLine[0] == 'f')
            {
                Debug.Log("f");
                matches = s.Scan(currentLine, 0);
                for (int j = 0; j < 3; j++)
                {
                    int temp = (Int32.Parse(matches[j].Value) - 1);
                    Debug.Log("三角索引:"+temp);
                    if (temp > vertexList.Count)
                        Debug.Log("越界节点:" + temp);
                    TrianglesList.Add(temp);
                }
                
                
            }
        }
        Debug.Log("匹配耗时:" + (System.Environment.TickCount - start_Regex));
        Debug.Log("vertexList.Count:" + vertexList.Count);
        Debug.Log("TrianglesList.Count:" + TrianglesList.Count);
        Debug.Log("赋值");
        Debug.Log("1");
        //mesh.vertices = vertexList.ToArray();
        Debug.Log("2");
        //mesh.triangles = TrianglesList.ToArray();
        Debug.Log("3");
        //o.GetComponent<MeshFilter>().mesh = mesh;
        Debug.Log("4");
        o.GetComponent<MeshFilter>().mesh.vertices = vertexList.ToArray();
        Debug.Log("5");
        o.GetComponent<MeshFilter>().mesh.triangles = TrianglesList.ToArray();
        Debug.Log("正则匹配耗时:" + (System.Environment.TickCount - start_Regex));
        Debug.Log("创建完毕");
        return o;
    }
    

1.3 速度对比

  • split()方法读取一个356KB的文件需要12641ms 这个函数仅需要5000ms 相当于速度提升一倍 但是这个方法还存在问题 当读取12.2M的文件时报错 以为马上开组会 所以还没来得及去找出bug 因为这个速率也是非常慢的 用户等待时间太长 因此不得不推荐下面这个插件 这部分bug会在后面进行补充待续!!

2.OBJ导入插件 Trilib

2.1 简介

  • 这是一个专用于导入3D模型的插件 速度非常快
    支持在运行状态下动态导入模型的插件,支持obj,fbx,stl等格式的模型

2.2 使用

2.2.1 界面展示

  • 提供9个现成的Demo场景 (暂时只用过第一个)
    在这里插入图片描述
    载入的界面在这里插入图片描述
    下面这个是导入的一个30MB的露骨OBJ文件,耗时小于5s
    在这里插入图片描述

2.2.2 使用

  • 只需要做两个简单修改 导入界面无需更改 但如果需要将其导入其他场景 需要在脚本"AssetLoaderWindows"的GoToSceneButtonClick()函数中修改为要导入的场景。
  • 在这里插入图片描述
  • 根据上述步骤 即可实现简单的模型导入场景,但是导入模型存在大小不合适 不能抓取等功能,需再进行调整
  • 调整物体(完整版代码)
void Start () {
        GameObject go = GameObject.Find("LoadedObject");
        Transform g = go.transform.GetChild(0);
        //清除与父物体间的关系
        g.SetParent(null);
        //Debug.Log(g != null);
        while (g.GetComponent<MeshFilter>().mesh.bounds.size.x * g.transform.localScale.x > 1.5 || g.GetComponent<MeshFilter>().mesh.bounds.size.y * g.transform.localScale.y > 1.5 || g.GetComponent<MeshFilter>().mesh.bounds.size.z * g.transform.localScale.z > 1.5)
        {
            //物体尺寸超过1.5米,则缩小物体尺寸10倍
            Debug.Log("ok");
            g.transform.localScale = g.transform.localScale / 10;
        }
        Debug.Log("0");
      
        ///<summary>结束后需要将物体标签改回Objects</summary>
        //go.tag = "Objects";
        ///<summary>设置为无父物体后 再添加组建"Choosable""Interactable"即可实现交互的基本条件</summary>
        GameObject defaultobject = GameObject.Find("defaultobject");
        if (defaultobject != null)
            Debug.Log(defaultobject);
        defaultobject.AddComponent<MeshCollider>();
        defaultobject.AddComponent<Interactable>();
        defaultobject.AddComponent<Choosable>();
        defaultobject.tag = "Objects";
        defaultobject.name = "Created in "+System.DateTime.Now.ToShortTimeString();
    }

2.3 导入的模型全黑

  • 简单表现为模型全为黑色,导致根本看不清楚细节
  • 如图所示(头骨):
    在这里插入图片描述

2.31 问题所在

  • 问题出现在缩放比例上 已经解决该问题
  • 见本博客
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值