原创作者:flowell,转载请标明出处:https://www.cnblogs.com/flowell/p/10838706.html


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 = ;
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 = ;           //获取需要渲染的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 >
? shapeInstance.StyleLabel
: shapeInstance.IfcTypeId * -;
            //处理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 > )
{
// 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 % == )
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() != )
{
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 > )
{
//当前待处理的节点
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 >
? shapeInstance.StyleLabel
: shapeInstance.IfcTypeId * -;
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 > )
{
// 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 > )
parentNode.Children = sonNodes.ToArray();
else
Debug.WriteLine(obj.GetType());
}
} return Build();
}

Xbim.GLTF源码解析(三):Builder类的更多相关文章

  1. Xbim.GLTF源码解析(一):简介

    原创作者:flowell,转载请标明出处:https://www.cnblogs.com/flowell/p/10838972.html 简介 Xbim.GLTF是将IFC文件转换成GLTF文件的一个 ...

  2. Xbim.GLTF源码解析(二):IFC和GLTF的对应关系

    原创作者:flowell,转载请标明出处:https://www.cnblogs.com/flowell/p/10839179.html IFC IFC是建筑信息模型(BIM)数据开放的国际标准,在建 ...

  3. Xbim.GLTF源码解析(四):轻量化处理

    原创作者:flowell,转载请标明出处:https://www.cnblogs.com/flowell/p/10839433.html 在IFC标准中,由IfcRepresentationMap支持 ...

  4. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  5. Celery 源码解析三: Task 对象的实现

    Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...

  6. ReactiveCocoa源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  7. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  8. React的React.createRef()/forwardRef()源码解析(三)

    1.refs三种使用用法 1.字符串 1.1 dom节点上使用 获取真实的dom节点 //使用步骤: 1. <input ref="stringRef" /> 2. t ...

  9. Mybatis源码解析3——核心类SqlSessionFactory,看完我悟了

    这是昨晚的武汉,晚上九点钟拍的,疫情又一次来袭,曾经熙熙攘攘的夜市也变得冷冷清清,但比前几周要好很多了.希望大家都能保护好自己,保护好身边的人,生活不可能像你想象的那么好,但也不会像你想象的那么糟. ...

随机推荐

  1. 构建之法——homework1:问题思考

    1.我看了第一章概论,1.2.4 软件工程的目标——创造“足够好”的软件,其中提到了什么是好的软件?  软件工程的一个要素就是把软件的Bug都消灭掉的过程. 提问:我们知道Bug是不可能完全消灭掉的, ...

  2. 探索ASP.NET Core 3.0系列一:新的项目文件、Program.cs和generic host

    前言:在这篇文章中我们来看看ASP.Net Core 3.0应用程序中一些基本的部分—— .csproj项目文件和Program.cs文件.我将会介绍它们从 ASP.NET Core 2.x 中的默认 ...

  3. 阿里云服务器CentOS6.9安装JDK

    1:首先查看系统有没有自带jdk rpm -qa | grep java 2:将存在的一一卸载 rpm -ev java-1.7.0-openjdk-1.7.0.141-2.6.10.1.el6_9. ...

  4. 构建于 B/S 端的 3D 摄像头可视化监控方案

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

  5. Ansible常用模块基本操作

    Ansible是一个系列文章,我会尽量以通俗易懂.诙谐幽默的总结方式给大家呈现这些枯燥的知识点,让学习变的有趣一些. 前言 对于任何一个框架,一个应用,为了更便于推广,便于使用,便于商业化,都会顺便提 ...

  6. 集合线性表--List之ArrayList

    集合操作——线性表 List: add().remove().subList().list.toArray().array.asList(). List排序:  Collections.sort(li ...

  7. 【原创】(七)Linux内存管理 - zoned page frame allocator - 2

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  8. springmvc处理局部异常和全局异常

    springmvc通过HandlerExceptionResolver(是一个接口,在spring-webmvc依赖下)处理程序异常,包括处理器异常.数据绑定异常以及处理器执行时发生的异常.Handl ...

  9. 500行代码,教你用python写个微信飞机大战

    这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手. 帮助蹲厕族.YP族.饭圈女孩在无聊之余可以有一样东西让他们振作起来!让他们的左手 / 右 ...

  10. 转:python2.x 和 python3.x的区别

    注:本文的原文地址为Key differences between Python 2.7.x and Python 3.x 许多 Python 初学者想知道他们应该从 Python 的哪个版本开始学习 ...