1. 程式人生 > >Xbim.GLTF源碼解析(三):Builder類

Xbim.GLTF源碼解析(三):Builder類

site tar render bsp ets Once 處理 multipl stop

IFC提取轉換成GLTF的邏輯在Builder類中,

Builder.BuildInstancedScene():該函數包含主要的轉換邏輯,對應於我們在第二章中最後提出的轉換算法流程,但是此處源代碼並沒有按照原本IFC中對象的組織形式將構件導出,而是按照扁平化的方式,將所有的構件一起導出,所以導出的GLTF中沒有保留原來對象的空間組織關系。在此先分析源代碼,然後我將給大家展示我修改後的代碼。

 //model:已打開的IFC實例,exclude:不導出的類型,EntityLabels:導出的構件ID
public gltf.Gltf BuildInstancedScene(IModel model, List<Type> exclude = null
, HashSet<int> EntityLebels = null) { Init(); Dictionary<int, ShapeComponentIds> geometries = new Dictionary<int, ShapeComponentIds>(); // this needs a previously meshed xbim file. //設置定時器 var s = new Stopwatch(); s.Start();
int iCnt = 0; Random r = new Random();

       //獲取不導出的類型
var excludedTypes = DefaultExclusions(model, exclude);

       //geomstore保存了該模型的幾何信息,通過BeginRead()函數返回的Reader訪問
using (var geomStore = model.GeometryStore) using (var geomReader = geomStore.BeginRead()) {
// process the materials and styles var sstyleIds = geomReader.StyleIds; foreach (var styleId in sstyleIds) { PrepareStyleMaterial(model, styleId); }
          
          
          // TODO: 無效代碼
int productLabel = 0;

          //獲取需要渲染的Shape,shape是真正的幾何圖元信息
var shapeInstances = GetShapeInstancesToRender(geomReader, excludedTypes, EntityLebels); // foreach (var shapeInstance in shapeInstances.OrderBy(x=>x.IfcProductLabel)) gltf.Mesh targetMesh = null;

          //遍歷每一個shape,IFC中的一個shape對應於gltf的一個mesh
foreach (var shapeInstance in shapeInstances.OrderBy(x => x.IfcProductLabel)) { if (CustomFilter != null) { var skip = CustomFilter(shapeInstance.IfcProductLabel, model); if (skip) continue; } // we start with a shape instance and then load its geometry. // a product (e.g. wall or window) in the scene returns: // - a node // - pointing to a mesh, with a transform // - 1 mesh // - with as many mesh primitives as needed to render the different parts // - pointers to the a material and accessors as needed // - 3 accessors per primitive // - vertices, normals, indices // - bufferviews can be reused by different accessors // - data in the buffer, of course if (productLabel != shapeInstance.IfcProductLabel) { // need new product // create node
              //_nodes數組保存著已經創建的node,所以新node的索引為數組的大小 var nodeIndex = _nodes.Count; var entity = model.Instances[shapeInstance.IfcProductLabel] as IIfcProduct; if (entity == null) { // fire error here. } var tnode = new gltf.Node(); tnode.Name = entity.Name + $" #{entity.EntityLabel}";
//獲取node的轉換矩陣 tnode.Matrix
= GetTransformInMeters(model, shapeInstance); // create mesh var meshIndex = _meshes.Count; targetMesh = new gltf.Mesh { Name = $"Instance {productLabel}" }; // link node to mesh
              //使node指向mesh tnode.Mesh = meshIndex; // add all to lists _nodes.Add(tnode); _meshes.Add(targetMesh);
              //此時mesh還未包含具體的圖元信息,在此之前只做了node和mesh的關聯 }
// now the geometry //填充mesh IXbimShapeGeometryData shapeGeom = geomReader.ShapeGeometry(shapeInstance.ShapeGeometryLabel); if (shapeGeom.Format != (byte)XbimGeometryType.PolyhedronBinary) continue;             
    
            //處理style
// work out colour id; // the colour is associated with the instance, not the geometry. // positives are styles, negatives are types var colId = shapeInstance.StyleLabel > 0 ? shapeInstance.StyleLabel : shapeInstance.IfcTypeId * -1;             //處理material int materialIndex; if (!styleDic.TryGetValue(colId, out materialIndex)) { // if the style is not available we build one by ExpressType materialIndex = PrepareTypeMaterial(model, shapeInstance.IfcTypeId); styleDic.Add(colId, materialIndex); } // note: at a first investigation it looks like the shapeInstance.Transformation is the same for all shapes of the same product             //一個shape(mesh)可以被多個構件(node)所引用
            //TODO: 經測試,以下的代碼存在BUG if (shapeGeom.ReferenceCount > 1) { // retain the information to reuse the map multiple times // // if g is not found in the dictionary then build it and add it ShapeComponentIds components; if (!geometries.TryGetValue(shapeGeom.ShapeLabel, out components)) { // mesh
                //XbimMesher類可以理解為geometry的容器,它保存幾何圖元的頂點,法向量和索引等信息 var xbimMesher = new XbimMesher(); xbimMesher.AddMesh(shapeGeom.ShapeData); components = AddGeom( xbimMesher.PositionsAsSingleList(model.ModelFactors.OneMeter), xbimMesher.Indices, xbimMesher.NormalsAsSingleList() ); geometries.Add(shapeGeom.ShapeLabel, components); } if (components != null) { var arr = GetTransformInMeters(model, shapeInstance); AddComponentsToMesh(targetMesh, components, materialIndex); } } else { // repeat the geometry only once // var xbimMesher = new XbimMesher(); xbimMesher.AddMesh(shapeGeom.ShapeData); var trsf = GetTransformInMeters(model, shapeInstance); var components = AddGeom( xbimMesher.PositionsAsSingleList(model.ModelFactors.OneMeter), xbimMesher.Indices, xbimMesher.NormalsAsSingleList() ); AddComponentsToMesh(targetMesh, components, materialIndex); } iCnt++; if (iCnt % 100 == 0) Debug.WriteLine($"added {iCnt} elements in {s.ElapsedMilliseconds}ms."); } } Debug.WriteLine($"added {iCnt} elements in {s.ElapsedMilliseconds}ms.");
       //構造GLTF,並返回
return Build(); }

修改後的Builder.BuildInstancedScene(),按照原本IFC中對象樹的組織方式,訪問IFC對象,同時導出GLTF對象,因此導出的GLTF是樹狀組織結構。

 /// <param name="model">The model needs to have the geometry meshes already cached</param>
        /// <param name="exclude">The types of elements that are going to be omitted (e.g. ifcSpaces).</param>
        /// <param name="EntityLebels">Only entities in the collection are exported; if null exports the whole model</param>
        /// <returns>GLTF, containing all message about the model</returns>
        public gltf.Gltf BuildInstancedScene(IModel model, List<Type> exclude = null, HashSet<int> EntityLabels = null)
        {

            //TODO: 需要確保context已經建立?
            using (var geomStore = model.GeometryStore)
            using (var geomReader = geomStore.BeginRead())
            {
                // process the materials and styles
                //保存material和style到builder類成員(內存中)
                var sstyleIds = geomReader.StyleIds;
                foreach (var styleId in sstyleIds)
                {
                    PrepareStyleMaterial(model, styleId);
                }

                Dictionary<int, ShapeComponentIds> geometries = new Dictionary<int, ShapeComponentIds>();
                Dictionary<int, int> shapes = new Dictionary<int, int>();
                HashSet<int> visitedLabels = new HashSet<int>();
                // this needs a previously meshed xbim file.

                var excludedTypes = DefaultExclusions(model, exclude);

                //獲取根節點
                var projects = model.Instances.OfType<IIfcProject>();
                if (projects.Count() != 1)
                {
                    Debug.WriteLine("Projects more than one.");
                }
                var project = projects.First();
                var projectRelations = project.IsDecomposedBy;

                Queue<IIfcObjectDefinition> objectQueue = new Queue<IIfcObjectDefinition>();
                Queue<int> nodeIndexQueue = new Queue<int>();

                //添加根節點
                var rootNode = new gltf.Node();
                rootNode.Name = project.Name + $"#{project.EntityLabel}";
                rootNode.Matrix = new[]
                {
                    1.0f,   0.0f,   0.0f,   0.0f,         //右手坐標系下相對坐標原點繞X軸旋轉90度(pi/4)
                    0.0f,   0.0f,  -1.0f,   0.0f,
                    0.0f,   1.0f,   0.0f,   0.0f,
                    0.0f,   0.0f,   0.0f,   1.0f
                };
                _nodes.Add(rootNode);
                List<int> rootSon = new List<int>();
                foreach (var rel in projectRelations)
                {
                    foreach (var relatedObject in rel.RelatedObjects)
                    {
                        var tnode = new gltf.Node();
                        var tnodeIndex = _nodes.Count;
                        _nodes.Add(tnode);
                        rootSon.Add(tnodeIndex);
                        nodeIndexQueue.Enqueue(tnodeIndex);
                        objectQueue.Enqueue(relatedObject);
                    }
                }
                rootNode.Children = rootSon.ToArray();

                //遍歷IFC節點樹,用參數隊列和節點隊列模擬遞歸順序訪問節點樹
                while (objectQueue.Count > 0)
                {
                    //當前待處理的節點
                    var obj = objectQueue.Dequeue();
                    var nodeIndex = nodeIndexQueue.Dequeue();
                    if (visitedLabels.Contains(obj.EntityLabel))
                        continue;
                    visitedLabels.Add(obj.EntityLabel);
                    List<int> sonNodes = new List<int>();

                    //處理當前節點包含的子節點,只有為IfcSpatialElement時才有relContained關系
                    var spatialElement = obj as IIfcSpatialElement;
                    if (spatialElement != null)
                    {
                        var containRelations = spatialElement.ContainsElements;
                        foreach (var containRelation in containRelations)
                        {
                            var containObjects = containRelation.RelatedElements;
                            foreach (var containObject in containObjects)
                            {
                                var tnode = new gltf.Node();
                                var tnodeIndex = _nodes.Count;
                                _nodes.Add(tnode);
                                sonNodes.Add(tnodeIndex);
                                nodeIndexQueue.Enqueue(tnodeIndex);
                                objectQueue.Enqueue(containObject);
                            }
                        }
                    }

                    //處理當前節點聚合的子節點
                    var aggregateRelations = obj.IsDecomposedBy;
                    foreach (var aggregateRelation in aggregateRelations)
                    {
                        var aggregateObjects = aggregateRelation.RelatedObjects;
                        foreach (var aggregateObject in aggregateObjects)
                        {
                            var tnode = new gltf.Node();
                            var tnodeIndex = _nodes.Count;
                            _nodes.Add(tnode);
                            sonNodes.Add(tnodeIndex);
                            nodeIndexQueue.Enqueue(tnodeIndex);
                            objectQueue.Enqueue(aggregateObject);
                        }
                    }

                    //處理當前節點
                    var parentNode = _nodes.ElementAt(nodeIndex);
                    parentNode.Name = obj.Name + $" #{obj.EntityLabel}";
                    if (typeof(IIfcProduct).IsInstanceOfType(obj))
                    {
//#endregion

                        //遍歷每一個shape
                        var shapeInstances = GetShapeInstancesToRender(geomReader, excludedTypes, new HashSet<int> { obj.EntityLabel });
                        foreach (var shapeInstance in shapeInstances.OrderBy(x => x.IfcProductLabel))
                        {

                            if (CustomFilter != null)
                            {
                                //TODO: 此處是productlabel,但是過濾器中的是ifc label,有何區別
                                var skip = CustomFilter(shapeInstance.IfcProductLabel, model);
                                if (skip)
                                    continue;
                            }
//instanceLabel 全為-1
                            //entityLabel 和 productLabel 全都一樣,都是一個產品的label,如一個椅子,有自己的entityLabel=ProductLabel。同時,相同外觀的椅子的label不一樣
                            //shapeGeometryLabel是產品構件的label,如一張椅子多個腿,每個腿有自己的shapeGeometryLabel,但是不同椅子有相同腿腳,它們的shapeGeometryLabel相同,所以不能用作篩選
                            //相同模型數據的節點只添加node,node包含轉換矩陣,然後返回,不添加數據
                            //A mesh is instantiated by node.mesh property. 
                            //The same mesh could be used by many nodes,
                            //which could have different transformations


                            // now the geometry
                            //提取對應label的圖形幾何信息
                            //只要以二進制存儲的幾何多邊形
                            IXbimShapeGeometryData shapeGeom = geomReader.ShapeGeometry(shapeInstance.ShapeGeometryLabel);

                            var entity = model.Instances[shapeInstance.IfcProductLabel] as IIfcProduct;     //Instance是模型的所有實例,一個PRODUCT(ENTITY)可以對應多個實例
                            if (entity == null)
                            { // fire error here. 
                            }
                            var tnode = new gltf.Node();
                            tnode.Name = entity.Name + $" #{entity.EntityLabel} #{shapeGeom.IfcShapeLabel}";
                            tnode.Matrix = GetTransformInMeters(model, shapeInstance);

                            // create mesh
                            var meshIndex = _meshes.Count;
                            var tnodeIndex = _nodes.Count;
                            sonNodes.Add(tnodeIndex);
                            if (shapes.TryGetValue(shapeInstance.ShapeGeometryLabel, out meshIndex))
                            {
                                tnode.Mesh = meshIndex;
                                _nodes.Add(tnode);
                                continue;
                            }
                            meshIndex = _meshes.Count;
                            var targetMesh = new gltf.Mesh
                            {
                                Name = $"Instance {shapeInstance.IfcProductLabel}"           //經過測試,produceLabel似乎和entityLabel一樣
                            };
                            // link node to mesh
                            tnode.Mesh = meshIndex;
                            // add all to lists
                            _nodes.Add(tnode);
                            _meshes.Add(targetMesh);
                            shapes.Add(shapeInstance.ShapeGeometryLabel, meshIndex);


                            if (shapeGeom.Format != (byte)XbimGeometryType.PolyhedronBinary)
                                continue;

                            // work out colour id; 
                            // the colour is associated with the instance, not the geometry.
                            // positives are styles, negatives are types
                            //存儲並獲取material
                            var colId = shapeInstance.StyleLabel > 0
                                ? shapeInstance.StyleLabel
                                : shapeInstance.IfcTypeId * -1;
                            int materialIndex;
                            if (!styleDic.TryGetValue(colId, out materialIndex))
                            {
                                // if the style is not available we build one by ExpressType
                                materialIndex = PrepareTypeMaterial(model, shapeInstance.IfcTypeId);
                                styleDic.Add(colId, materialIndex);
                            }


                            // note: at a first investigation it looks like the shapeInstance.Transformation is the same for all shapes of the same product
                            //如果這個shape被引用的次數大於1(只要被product引用了,就至少為1,即最小值為1),那麽這個shape可能在之前已經被添加過了
                            if (shapeGeom.ReferenceCount > 1)
                            {
                                // retain the information to reuse the map multiple times
                                //

                                // if g is not found in the dictionary then build it and add it
                                ShapeComponentIds components;
                                if (!geometries.TryGetValue(shapeGeom.ShapeLabel, out components))
                                {
                                    // mesh 
                                    var xbimMesher = new XbimMesher();
                                    xbimMesher.AddMesh(shapeGeom.ShapeData);

                                    components = AddGeom(
                                        xbimMesher.PositionsAsSingleList(model.ModelFactors.OneMeter),
                                        xbimMesher.Indices,
                                        xbimMesher.NormalsAsSingleList()
                                        );
                                    geometries.Add(shapeGeom.ShapeLabel, components);
                                }

                                //添加頂點等信息(primitive)到mesh中
                                if (components != null)
                                {
                                    var arr = GetTransformInMeters(model, shapeInstance);               //TODO: ???unused
                                    AddComponentsToMesh(targetMesh, components, materialIndex);         //此處添加了實際的mesh
                                }                                                                       //不應該這樣做,應該不添加mesh,只在node中添加同樣mesh的索引即可
                            }
                            else//這個shape的被引用次數小於等於一,那麽這個shape在此之前肯定還沒有被添加,直接添加即可。
                            {   //TODO: ??? 這兩段if-else代碼似乎沒有區別呀!
                                // repeat the geometry only once
                                var xbimMesher = new XbimMesher();
                                xbimMesher.AddMesh(shapeGeom.ShapeData);
                                var trsf = GetTransformInMeters(model, shapeInstance);
                                var components = AddGeom(
                                        xbimMesher.PositionsAsSingleList(model.ModelFactors.OneMeter),
                                        xbimMesher.Indices,
                                        xbimMesher.NormalsAsSingleList()
                                        );
                                AddComponentsToMesh(targetMesh, components, materialIndex);
                            }

                        }
                    }

                    //為當前節點(gltf.node)添加子節點(node.children),建立起父子關系
                    if (sonNodes.Count > 0)
                        parentNode.Children = sonNodes.ToArray();
                    else
                        Debug.WriteLine(obj.GetType());
                }
            }

            return Build();
        }

Xbim.GLTF源碼解析(三):Builder類