在unity中读取ifc文件模型

由于xbim是基于.net framework的一款ifc开源库,且unity同样支持C#语言的编写,故使用xbim在unity中解析.ifc文件。本文主要讲解如何在unity中构建三维模型。

使用xbim提供方法

const string fileName = @"SampleHouse4.ifc";
var wexBimFilename = Path.ChangeExtension(fileName, "wexBIM");
var xbimDbFilename = Path.ChangeExtension(fileName, "xBIM");

// Make sure we are using an IModel implementation that supports saving of '.xbim' files
IfcStore.ModelProviderFactory.UseHeuristicModelProvider();
	
using (var model = IfcStore.Open(fileName))
{
	// IFC file is already parsed and open. Now build the 3D
	var context = new Xbim3DModelContext(model);
	context.CreateContext();	// Creates the Geometry using native GeometryEngine

	// Optional: Export to 'wexbim' format for use in WebUI's xViewer - geometry only
	using (var wexBimFile = File.Create(wexBimFilename))
	{
		using (var wexBimBinaryWriter = new BinaryWriter(wexBimFile))
		{
			model.SaveAsWexBim(wexBimBinaryWriter);
			wexBimBinaryWriter.Close();
		}
		wexBimFile.Close();
	}
		
	// Save IFC to the internal XBIM format, which includes geometry
	model.SaveAs(xbimDbFilename, StorageType.Xbim);
}

然而此种方法在unity中会导致unity闪退,原因见此:因为xbim.Geomotry重写了模型构建的方法,与unity内置模型构建方法冲突,故context.CreateContext()方法与unity冲突。但由于xbim是基于.net framework的开源库,而此bug则是xbim和unity的冲突,故开发者不予以解决。

我的方法

因为context.CreateContext()在unity中会报错,故要在一个新的项目中调用此语句生成中间文件,而后把中间文件解析获得模型。

wexbim

为适配web端,xbim团队开发了wexbim。因为weixbim文件储存的是且全是ifc文件的几何数据,故这是一个很好的中间文件来生成模型。至于如何生成wexbim文件,见前文超链接。

解析wexbim

如何解析wexbim则要看wexbim如何生成,建议可以根据xbim的源码进行查看。model.SaveAsWexBim(wexBimBinaryWriter);是生成wexbim的语句,这里使用的是Binary Write进行二进制数字的写入。
读取wexbim的代码见下:

    public void ReadWexbimFile(string  fileName)
    {
    	float scale;
    	Vector3 offsite;
        using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            using (var br = new BinaryReader(fs))
            {
                var magicNumber = br.ReadInt32();
                var version = br.ReadByte();
                var shapeCount = br.ReadInt32();
                var vertexCount = br.ReadInt32();
                var triangleCount = br.ReadInt32();
                var matrixCount = br.ReadInt32();
                var productCount = br.ReadInt32();
                var styleCount = br.ReadInt32();
                var meter = br.ReadSingle();
                var regionCount = br.ReadInt16();
                scale = meter;
                
                //Region
                for (int i = 0; i < regionCount; i++)
                {
                    var population = br.ReadInt32();
                    var centreX = br.ReadSingle(); centreX /= scale;
                    var centreY = br.ReadSingle(); centreY /= scale;
                    var centreZ = br.ReadSingle(); centreZ /= scale;
                    var boundsBytes = br.ReadBytes(6 * sizeof(float));
                    var bounds = XbimRect3D.FromArray(boundsBytes); 
                    offsite = regions[0].position;              
                }

                //texture
                for (int i = 0; i < styleCount; i++)
                {
                    var styleId = br.ReadInt32();
                    var red = br.ReadSingle();
                    var green = br.ReadSingle();
                    var blue = br.ReadSingle();
                    var alpha = br.ReadSingle();
                }

                //product
                for (int i = 0; i < productCount; i++)
                {
                    var entityLabel = br.ReadInt32();
                    var typeId = br.ReadInt16();
                    var boxBytes = br.ReadBytes(6 * sizeof(float));
                    XbimRect3D bb = XbimRect3D.FromArray(boxBytes);
                }

                //shape
                for (int i = 0; i < shapeCount; i++)
                {
                    var shapeRepetition = br.ReadInt32();
                    if (shapeRepetition > 1)
                    {
                        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));
                        }
                        var triangulation = br.ReadShapeTriangulation();
                    }
                    else if (shapeRepetition == 1)
                    {
                        var ifcProductLabel = br.ReadInt32();
                        var ifcTypeId = br.ReadInt16();
                        var instanceLabel = br.ReadInt32();
                        var styleLabel = br.ReadInt32();                  
                        XbimShapeTriangulation triangulation = br.ReadShapeTriangulation();
                    }
                }
            }
        }            
    }

wexbim主要储存了region,texture,product,shapeInstance,shapeTriangulation这些数据类型,而后根据前面储存的相应类型的数量来遍历获取对应的数据。

  1. region相当于模型的整体范围,储存了:population,center X,center Y,centerZ,BoundingBoxXYZ是中心点坐标、boundingBox为方形包围盒(最小点和最大点的坐标)。
  2. texture储存了对应的颜色信息,储存了:r,g,b,a,styleId。不同的颜色对应不相同的id。
  3. product则是模型的一些实体组件(门,窗,墙壁等),储存了:entiityLabel,typeId,boundingBoxentityLabel为对应实体的label,每个product拥有一个专属的label。tyepId则是product的类型的id,所有的门共享相同的id,boundingBox则是方形包围盒见上。
  4. shapeInstance可以理解为unity中的一个mesh所对应的gameObject。例如一个窗户,可能包含3-4个mesh(窗框、玻璃等)故需要3-4个不同的gameObject,则这一个gameObeject就是一个shapeInstanceshapeInstance中储存的是:ifcProductLabel,ifcTypeId,instanceLabel,styleLabel,transformation/*可选*/,shapeTriangulationifcProductLabel用于对照之前product.entityLabel来获取productshapeInstance 的对应关系(一个product对应一到多个shapeInstance);ifcTypeIdproduct.typeId同理;instanceLabel则是这个shapeInstance的label无用;styleLabel用来和前文texture.styleId来对应用来添加material。
  5. shapeTriangulation是对应的mesh信息,它里面储存的数据需要读取源码才能知道,这里笔者将其写出来:vertices,faces同时faces里存储了indices,normalsvertices是mesh对应顶点在世界坐标系中的xyz坐标,faces是mesh中对应的面数,indices为绘制mesh时的三角形索引,normals为法线信息。

我们可以看到,在读取shapeInstance的时候我们需要先判断shpeRepetition的值,大于1是一部分,等于1是另一部分。这里来解读一下这个shapeRepetition的玄机。

在构建建筑模型的时候,有些实体会大量重复的使用,例如居民楼模型上的所有门窗和部分家具。如果把这些门窗的triangulation信息都单独存储会占用大量不必要的空间,故wexbim在这里进行了一下优化,即shapeRepetitonshapeRepetition是对应mesh的重复信息,例如这个模型中有三个一模一样的的窗户,则在储存窗户时,对应的shapeRepetition=3,这个时候对于这三个窗户而言,储存了他们各自的ifcProductLabel,ifcTypeId,instanceLabel,styleLabel,transform和一个共用的shapeTriangulation

如果忽略transform直接加载模型信息,则由于这三个窗户共享相同的shapeTriangulation他们会加载在相同的地方,此时就要使用我们储存的transform信息。transform是一个4x4的Matrix,用来进行模型的各种旋转平移缩放等操作。于是,用几个matrix就可以不储存复杂的三角形信息,进而节省了大量的空间。xbim也提供了相应的方法用来帮助我们解析模型:XbimShapeTriangulation.Transform(XbimMatrix3D matrix)

至此我们已经读取了模型的所有几何信息,下章我将会将一下模型的重构。

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

猜你喜欢

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