原創做者:flowell,轉載請標明出處:http://www.javashuo.com/article/p-xuqkeenv-e.htmlhtml
IFC提取轉換成GLTF的邏輯在Builder類中,node
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(); }