Unity Mesh绘制点云(参考PCX)

PCX是Unity开源的点云绘制,操作简单,拖入ply文件即可得到点云prefab,但是只支持binary_little_endian编码的ply格式点云。
我手上只有一些xyzrgb并且是txt格式的点云文件需要显示,网上其他的一些mesh绘制需要考虑文件位置及绘制点数问题,所以参考PCX的部分源码。
直接放上源码吧
IndexFormat.UInt32可以绘制40亿个顶点,但有些设备不支持该格式
本段代码需将txt后缀变为point,且点数数据格式为x y z r g b。
替换的后缀代码中可自由替换,数据格式可调整代码读取不同格式点云

// An highlighted block


using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Linq;
using UnityEngine.Rendering;
using UnityEditor;
using System;
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;
#else
using UnityEditor.Experimental.AssetImporters;
#endif



//ScriptedImporter 资产导入脚本
//[ScriptedImporter(1, "point")] 导入文件类型
namespace pointcould_display
{
    
    
    [ScriptedImporter(1, "point")]
    public class meshrenderpcl : ScriptedImporter
    {
    
    
        #region ScriptedImporter implementation
        //int numPoints = 60000;

        //读取点云xyz列表,颜色列表
        List<Vector3> points = new List<Vector3>();
        List<Color> colors = new List<Color>();

        //点云数量,生成mesh时需要判断点云数量使用不同网格索引缓冲区
        int num = 0;

        //导入重写虚函数OnImportAsset
        public override void OnImportAsset(AssetImportContext context)
        {
    
    
            //读取点云数据
            //context.assetPath 导入文件的路径
            ReadPointColudData(context.assetPath);
            //var data = ImportAsPointCloudData(context.assetPath,points,colors);
            //生成点云
            var gameObject = new GameObject();
            var mesh = CreateMesh(context.assetPath, points, colors, num);

            //设置mesh
            var meshFilter = gameObject.AddComponent<MeshFilter>();
            meshFilter.sharedMesh = mesh;

            //设置材质
            var meshRenderer = gameObject.AddComponent<MeshRenderer>();
            meshRenderer.sharedMaterial = GetDefaultMaterial();

            //在导入事件期间定义脚本化导入器的导入上下文。、
            //AddObjectToAsset	向导入操作的结果添加对象。
            //SetMainObject 设置导入的主要对象。
            context.AddObjectToAsset("prefab", gameObject);
            if (mesh != null) context.AddObjectToAsset("mesh", mesh);
            context.SetMainObject(gameObject);


        }
        #endregion
        void ReadPointColudData(string fileAddress)
        {
    
    

            //读取点云数据
            FileInfo fInfo0 = new FileInfo(fileAddress);

            string s = "";
            StreamReader r;


            if (fInfo0.Exists)
            {
    
    
                r = new StreamReader(fileAddress);
            }
            else
            {
    
    
                Debug.LogError("NO THIS FILE!");
                return;
            }
            // 将文本中的点云数据读入分别存到xyz数组和rgb数组中
            while ((s = r.ReadLine()) != null)
            {
    
    
                string[] words = s.Split(" "[0]);

                Vector3 xyz = new Vector3(float.Parse(words[0]), float.Parse(words[1]), float.Parse(words[2]));
               

                points.Add(xyz);

               if(words.Length < 4)
               {
    
    
                    List<String> b = words.ToList();
                    b.Add("255");
                    b.Add("255");
                    b.Add("255");
                    words = b.ToArray();
                }

               Color colorRGB = new Color(float.Parse(words[3] != null ? words[3] : "255") / 255.0f, float.Parse(words[4] != null ? words[4] : "255") / 255.0f, float.Parse(words[5] != null ? words[5] : "255") / 255.0f);
                colors.Add(colorRGB);
                //Debug.Log(xyz.ToString() + "," + colorRGB.ToString());
            }

            //总顶点数
            num = points.Count;
        }


        Mesh CreateMesh(string path, List<Vector3> points, List<Color> colors, int pointsNum)
        {
    
    
            Mesh mesh = new Mesh();
          
            //获取名称
            mesh.name = Path.GetFileNameWithoutExtension(path);

            //网格索引缓冲区数据的格式。16为只能65535个顶点,32为更大
            mesh.indexFormat = pointsNum > 65535 ?
                        IndexFormat.UInt32 : IndexFormat.UInt16;
            
            //设置网格顶点及颜色
            mesh.SetVertices(points);
            mesh.SetColors(colors);

            //为子网格设置索引缓冲区。
            //子网格表示使用单个 Material 渲染的三角形(或具有不同 MeshTopology 的索引)的列表。
            //当网格与具有多个材质的 Renderer 一起使用时,应确保每个材质有一个子网格。
            mesh.SetIndices(
                Enumerable.Range(0, pointsNum).ToArray(),
                MeshTopology.Points, 0
            );


            //将以前进行的网格修改上传到图形 AP
            //从代码创建或修改网格(使用 vertices、normals、triangles 等属性)时,
            //网格数据在内部标记为“已修改”,会在下次渲染网格时发送给图形 API。
            mesh.UploadMeshData(true);
            return mesh;
        }

        static Material GetDefaultMaterial()
        {
    
    
            //? 1.定义数据类型可为空 2.用于判断对象是否为空,如果对象为空,则无论该对象调用什么皆不会抛出异常,直接返回null(C#6.0)
            //?? 可用于判断一个变量在为null时返回一个指定的值
            // Via package manager
            var path_upm = "Packages/jp.keijiro.pcx/Editor/Default Point.mat";
            // Via project asset database
            var path_prj = "Assets/Pcx/Editor/Default Point.mat";
            return AssetDatabase.LoadAssetAtPath<Material>(path_upm) ??
                   AssetDatabase.LoadAssetAtPath<Material>(path_prj);
        }

       
    }
}

注意1:将文件拖入即可,但是unity有txt导入功能的,所以要修改后缀,修改的后缀可自行更换,[ScriptedImporter(1, “point”)]即将txt改为point,可以修改point为其他后缀

注意2:脚本创建后放入Editor文件夹,因为导入是属于Editor模块,因为Mesh已经上传过GUI了,不需要考虑点云文件打包

附:PCX mat放入代码中的mat位置及几个标准点云
链接: PCX相关下载

猜你喜欢

转载自blog.csdn.net/banjuhuaxianduo/article/details/126426847
今日推荐