基于xbim在unity中生成模型

所需数据结构

根据上一篇文章,我们得知在解析wexbim时,我们主要获取了region,color,product,shapeInstance,faceTriangulation故我们要先构建他们相应的数据结构,这里我分别用了五个structure来储存他们。

//储存读取的region数据
public struct MyBimRegion
{
    public int population;
    public Vector3 position;
    public Vector3 scale;
    public XbimRegion bimRegion;
    public MyBimRegion(int population, float px, float py, float pz, float sx, float sy, float sz)
    {
        this.population = population;
        position = new Vector3(px, py, pz);
        scale = new Vector3(sx, sy, sz);
        bimRegion = new XbimRegion()
        {
            Population = population,
            Centre = new XbimPoint3D(px, py, pz),
            Size = new XbimVector3D(sx, sy, sz)
        };
    }
}

//储存读取的color数据
public struct MyBimColor
{
    public int styleLabel;
    public float r, g, b, a;
    public Color color;
    public XbimColour xbimColour;
    public MyBimColor(int label, float r, float g, float b, float a = 1)
    {
        styleLabel = label;
        this.r = r; this.g = g;
        this.b = b; this.a = a;
        color = new Color(r, g, b, a);
        xbimColour = new XbimColour(r, g, b, a);
    }
}

//储存读取的product数据
public struct MyBimProduct
{
    public int entityLabel;
    public short typeId;
    public Vector3 position;
    public Vector3 scale;
    public List<MyBimShape> shapes;

    public MyBimProduct(int label, short id, Vector3 pos, Vector3 scale)
    {
        entityLabel = label; 
        typeId = id;
        position = pos;
        this.scale = scale;
        shapes = new List<MyBimShape>();
    }

    public void AddShape(MyBimShape shape)
    {
        shapes.Add(shape);
    }
}

//储存读取的shapeInstance数据
public struct MyBimShape 
{
    public XbimShapeInstance shape;
    public int ifcProductLabel;
    public short ifcTypeId;
    public int instanceLabel;
    public int styleLabel;
    public List<MyBimTriangulation> triangulations;
    public XbimMatrix3D transform;
    public MyBimShape(int productLabel, short typeId, int instanceLabel,int styleLabel,XbimMatrix3D matrix)
    {
        this.ifcProductLabel = productLabel;
        this.ifcTypeId = typeId;
        this.instanceLabel = instanceLabel;
        this.styleLabel = styleLabel;
        this.triangulations = new List<MyBimTriangulation>();
        shape = new XbimShapeInstance(instanceLabel)
        {
            IfcProductLabel = productLabel,
            IfcTypeId = typeId,
            StyleLabel = styleLabel,
            Transformation = matrix
        };
        transform = matrix;
    }

    public void AddTriangulation(MyBimTriangulation ta)
    {
        triangulations.Add(ta);
    }
}

//储存读取的shapeTriangulation数据
public struct MyBimTriangulation
{
    public XbimShapeTriangulation bimTriangulation;
    public List<Vector3> vertices;

    public List<int> triangles;
    public List<Vector3> normals;
    public MyBimTriangulation(XbimShapeTriangulation triangulation, float scale, Vector3 offsite, XbimMatrix3D matrix = new XbimMatrix3D(), bool bMatrix = false)
    {
    	//判断是否需要对三角形进行几何操作
        if (bMatrix)
        {
            bimTriangulation = triangulation.Transform(matrix);
        }
        else
            bimTriangulation = triangulation;

        vertices = new List<Vector3>();
        triangles = new List<int>();
        normals = new List<Vector3>();
        foreach (var v in bimTriangulation.Vertices)
        {
            vertices.Add(new Vector3((float)v.X, (float)v.Y, (float)v.Z) / scale - offsite);
        }
        foreach(var f in bimTriangulation.Faces)
        {
            triangles.AddRange(f.Indices);
            foreach (var n in f.Normals)
            {
                normals.Add(new Vector3((float)n.Normal.X, (float)n.Normal.Y, (float)n.Normal.Z));
            }
        }
    }
}

存储读取的数据

而后在解析wexbim文件时用List把相应的数据储存起来。
tips1:在存储坐标数据时,记得除以最开始读取的meter值,meter代表了游戏中的一个坐标单位和真实世界中1米的对应比例,如果不除的话,模型可能过大
tips2:region的center坐标是整体模型的偏移量,如果想要每次模型的中心都在原点处记得对所有的坐标都减去region的center坐标

    List<MyBimColor> colors = new List<MyBimColor>();
    List<MyBimShape> shapeInstances = new List<MyBimShape>();
    List<MyBimTriangulation> triangulations = new List<MyBimTriangulation>();
    List<MyBimRegion> regions = new List<MyBimRegion>();
    List<MyBimProduct> products = new List<MyBimProduct>();

在for循环解析wexbim文件时,将对应的数据存储其中方便之后的模型重建。
这里主要讲一下shapeInstance的存储。

if (shapeRepetition > 1)
{
	List<MyBimShape> myshapes = new List<MyBimShape>();
	for (int j = 0; j < shapeRepetition; j++)
	{
	//读取数据
	var ifcProductLabel = br.ReadInt32();
	var ifcTypeId = br.ReadInt16();
    var instanceLabel = br.ReadInt32();
    var styleLabel = br.ReadInt32();
    var transform = XbimMatrix3D.FromArray(br.ReadBytes(sizeof(double) * 16));
    
	//将读取的shapeInstance添加到list中,并根据label获得他的product
	myShape = new MyBimShape(ifcProductLabel, ifcTypeId, instanceLabel, styleLabel, transform);
	shapeInstances.Add(myShape);
    myshapes.Add(myShape);
    var p = products.Find(product => product.entityLabel == ifcProductLabel);
    p.AddShape(myShape);
	}
    var triangulation = br.ReadShapeTriangulation();
    //给这些重复的shapeInstance分别添加对应的三角形,并在三角形初始化时进行几何操作
	foreach (var ms in myshapes)
	{
		var tri = new MyBimTriangulation(triangulation, scale, offsite, ms.transform, true);
		ms.AddTriangulation(tri);
		triangulations.Add(tri);
	}
}

由于shapeInstance中含有较多的label和id故需要以它为中心把其它数据给连接起来。
因为shapeInstance相当于是product的一个部分,故在shapeInstance初始化时就把它添加给对应的prooduct
同时shapeInstance要包含相应的mesh信息,故在读取triangulation时,也将对应的shapeInstance添加对应的triangulation

模型的生成

模型的生成主要依靠的是unity的mesh类。方法如下:

  1. 遍历products,生成相应的空的gameObject(物体名字可为product.EntityLabel方便后面查找使用)
  2. 再遍历product.shapes,生成相应的带有mesh的gameObject。并将这些新生成的gameObject的父对象设置为对应的product的gameObject
  3. 再将所有的product放置在一个空物体下,旋转(-90,0,0)的欧拉角。这是因为ifc文件是z轴向上,而unity则是y轴向上。

代码如下:

    List<GameObject> productGO = new List<GameObject>();
    GameObject mainGO;
    public void GenerateProduct(MyBimProduct product)
    {
        var go = new GameObject
        {
            name = product.entityLabel.ToString()
        };
        go.transform.position = product.position;
        go.transform.localScale = product.scale;
        productGO.Add(go);
        foreach (var s in product.shapes)
            GenerateShape(s);
        go.transform.parent = mainGO.transform;
    }

    private void GenerateShape(MyBimShape shape)
    {
        GameObject shapeGO = new GameObject(shape.instanceLabel.ToString());
        var go = productGO.Find(p => p.name == shape.ifcProductLabel.ToString());
        shapeGO.transform.parent = go.transform;

		//添加mesh
        var mf = shapeGO.AddComponent<MeshFilter>();
        var mesh = mf.mesh;
        foreach(var tri in shape.triangulations)
        {
            mesh.vertices = tri.vertices.ToArray();
            mesh.triangles = tri.triangles.ToArray();
            mesh.Optimize();
            mesh.RecalculateNormals();
        }
        
        //添加texture
        var mr = shapeGO.AddComponent<MeshRenderer>();
        var mat = new Material(Shader.Find("Standard"));     
        var c = colors.Find(cl => cl.styleLabel == shape.styleLabel);
        mat.color = c.color;
        mr.material = mat;  
    }

在添加mesh的时候我只是读取了triangulation的vertices和triangles这两个值,并未读取当时存储的normals。

这是因为:wex在存储法线时是按照一个面一个面的存储法线,这就导致了如果这个点是被多个面共享的话,那么这个点的法线就被多次存储,即tri.vertices.Count<=tri.normals.Count但unity中mesh.vertices.Count==mesh.normals.Count,如不相等会报错。故在这里使用了unity的mesh.RecalculateNormals()方法,发现效果还不错就先暂时这样解决,如果以后有时间会进一步修改。

最后整体在start方法中的调用如下:

    void Start()
    {
        mainGO = new GameObject();
        ReadWexbimFile(fileName);
        foreach (var product in products)
            GenerateProduct(product);
        mainGO.transform.rotation = Quaternion.Euler(-90, 0, 0);
    }

效果图

下面是六种不同类型的模型,大家可以使用UnityShader对模型进行进一步的渲染优化。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

发布了5 篇原创文章 · 获赞 3 · 访问量 165

猜你喜欢

转载自blog.csdn.net/qq_38015139/article/details/104824454