获取Revit模型中构件几何之间的引用关系

本文介绍了一种利用Revit SDK提升模型导出效率的方法,通过解析GeometryInstance来识别重复几何,减少文件大小并加快导出速度。文章还提供了一个具体的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Revit SDK给我们提供了接口IExportContext用于将模型导出,从接口中我们可以获取每个构件的三角片信息,材质信息,只需要将每个构件三角片化即可。但是当模型较大的时候就会发现导出速度慢,导出的文件较大,这个时候有没有办法提升导出速度,减少导出文件的大小呢?是有这样的办法的,Revit在内部存储构件的几何信息时,相同几何的构件只存储的一份几何信息,这样子可以减少rvt文件大小。

(1)那我们如何获取这个构件之间的引用关系呢?答案就在GeometryIntance里,Revit SDK对GeometryIntance的描述如下:

A GeometryInstance represents a set of geometry stored by Revit in a default configuration, and then transformed into the proper location as a result of the properties of the element. The most common situation where GeometryInstances are encountered is in Family instances. Revit uses GeometryInstances to allow it to store a single copy of the geometry for a given family and reuse it in multiple instances. Note that not all Family instances will include GeometryInstances. When Revit needs to make a unique copy of the family geometry for a given instance (because of the effect of local joins, intersections, and other factors related to the instance placement) no GeometryInstance will be encountered; instead the Solid geometry will be found at the top level of the hierarchy. A GeometryInstance offers the ability to read its geometry through the GetSymbolGeometry() and GetInstanceGeometry() methods. These methods return another Autodesk.Revit.DB.GeometryElement which can be parsed just like the first level return.

这段话写的很清楚,就是如果两个构件存在相同几何,就会只存储一份几何,怎么得出两个构件的GeometryInstance的几何是一样的呢?答案就在GeometryInstance的Symbol这个属性里,如果两个GeometryIntance的Symbol的Id一样,则这两个构件就是引用关系。如果没有引用关系,则图元的Geometry里就没有GeometryInstance对象,只有Line,Solid等对象。一般来说非FamilyInstance构件没有引用关系,构件的引用关系只存在于FamilyInstance构件之间。

(2)知道了两个对象之前的引用关系,还需要转换矩阵,就是从一个构件通过转换矩阵可以转换到另外一个构件的位置。需

要通过FamilyInstance的GetTransform方法以及HandFlipped和FaceFlipped两个属性结合判断。

下面上代码:

using Autodesk.Revit.DB;
using HWModel.HWXDB;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace RevitPlug
{
    class GeometryExpoter : IExportContext
    {
        public GeometryExpoter(Document doc)
        {
            curDoc = doc;
        }

        private Document curDoc {get;set;}

        private bool isRefInstance = false;

        private bool isSkipped = false;

        private Stack<Transform> transforms = new Stack<Transform>();

        private Element curElement;
        public StreamWriter sw { get; set; }

        private GeometryTemp geometryTemp = new GeometryTemp();

        private ElementId geometrySymbolId;

        public void Finish()
        {
            sw.WriteLine("**************Finish*****************");
        }

        public bool IsCanceled()
        {
            //sw.WriteLine("IsCanceled");
            return false;
        }

        private ElementId GetGeometrySymbolId(Element e)
        {
            Options option = new Options();
            option.ComputeReferences = true;
            option.DetailLevel = ViewDetailLevel.Fine;
            
            GeometryElement geomElement  = e.get_Geometry(option);
            

            int geometryElementSize = 0;
            ElementId symbolId = ElementId.InvalidElementId;

            foreach (GeometryObject geoObj in geomElement)
            {
                geometryElementSize++;
                if (geometryElementSize > 1)
                    return ElementId.InvalidElementId;
                
                if (geoObj is GeometryInstance)
                {
                    GeometryInstance geoInstance = geoObj as GeometryInstance;
                    symbolId = geoInstance.Symbol.Id;
                }
            }

            return symbolId;
        }

        //计算FamilyInstance的转换矩阵
        private Transform GetFamilyInstanceTransform(FamilyInstance familyInstance)
        {
            if (familyInstance == null)
                throw new ArgumentNullException();

            var trans = familyInstance.GetTransform();
            var tempTran = Transform.Identity;
            tempTran.BasisX = familyInstance.HandFlipped ? XYZ.BasisX * (-1) : XYZ.BasisX;
            tempTran.BasisY = familyInstance.FacingFlipped ? XYZ.BasisY * (-1) : XYZ.BasisY;
            var totalTran = trans.Multiply(tempTran);
            return totalTran;
        }

        //计算从familyInstance1到familyInstance2的转换矩阵
        private Transform GetRelaitonTransform(FamilyInstance familyInstance1,FamilyInstance familyInstance2)
        {
            var totalTran1 = GetFamilyInstanceTransform(familyInstance1);
            var totalTran2 = GetFamilyInstanceTransform(familyInstance2);
            var trans = totalTran2.Multiply(totalTran1.Inverse);
            return trans;
        }

        public RenderNodeAction OnElementBegin(ElementId elementId)
        {
            sw.WriteLine("----------------OnElementBegin,ElementId:" + elementId.IntegerValue);
            sw.Flush();
            geometrySymbolId = ElementId.InvalidElementId;

            geometryTemp.clear();
            curElement = curDoc.GetElement(elementId);
            
            isSkipped = false;
            isRefInstance = false;

            //如果是嵌板
            if(curElement is Autodesk.Revit.DB.FamilyInstance)
            {
                Autodesk.Revit.DB.FamilyInstance familyInstace = curElement as Autodesk.Revit.DB.FamilyInstance;

                sw.WriteLine("----------------OnElementBegin,geometrySymbolIdStart:");
                sw.Flush();
                sw.WriteLine("is null:" + familyInstace == null);
                sw.Flush();
                geometrySymbolId = GetGeometrySymbolId(familyInstace);

                sw.WriteLine("----------------OnElementBegin,geometrySymbolId:" + geometrySymbolId.IntegerValue);
                sw.Flush();

                int geometryId =0;
                //如果是
                if(geometrySymbolId==ElementId.InvalidElementId
                    ||!XDBManager.GetInstance().eleGrometry.TryGetValue(geometrySymbolId.IntegerValue, out geometryId))
                {
                    
                    sw.WriteLine("----------------OnElementBegin,ElementId:" + elementId.IntegerValue);
                    return RenderNodeAction.Proceed;
                }
                else
                {
                    isRefInstance = true;
                    return RenderNodeAction.Skip;
                }
            }
            else
            {
                isRefInstance = false;
                return RenderNodeAction.Skip;
            }
        }

        private string FromatTransform(Transform t)
        {
            return string.Format("[" +
                "{0},{1},{2},{3}," +
                "{4},{5},{6},{7}," +
                "{8},{9},{10},{11}," +
                "{12},{13},{14},{15}" +
                "]",
                t.BasisX.X, t.BasisX.Y, t.BasisX.Z, 0,
                t.BasisY.X, t.BasisY.Y, t.BasisY.Z, 0,
                t.BasisZ.X, t.BasisZ.Y, t.BasisZ.Z, 0,
                t.Origin.X * 304.8, t.Origin.Y * 304.8, t.Origin.Z * 304.8, 1).ToString();
        }

        public void OnElementEnd(ElementId elementId)
        {
            sw.WriteLine("----------------OnElementEnd,ElementId:" + elementId.IntegerValue);
            sw.WriteLine("----------------OnElementEnd,跳过:{0},引用{1}:", isSkipped, isRefInstance);
            sw.Flush();

            //如果构件被跳过,或者不是引用构件且几何为空,则直接不处理
            if (isSkipped||(!isRefInstance && geometryTemp.IsEmpty()))
                return;
            
            int geometryId = curElement.Id.IntegerValue;

            string transfromJson = FromatTransform(Transform.Identity);

            if(isRefInstance)
            {
                geometryId = XDBManager.GetInstance().eleGrometry[geometrySymbolId.IntegerValue];
                //计算转换矩阵
                ElementId baseEleId = new ElementId(geometryId);
                Autodesk.Revit.DB.FamilyInstance baseEle = curDoc.GetElement(baseEleId) as FamilyInstance;
                var trans = GetRelaitonTransform(baseEle, curElement as FamilyInstance);
                
                transfromJson = FromatTransform(trans);
            }
            else
            {
                Geometry geometry = geometryTemp.ConvertToHWGeometry(geometryId);
                //如果几何族类型存在,则加入
                if (geometrySymbolId != ElementId.InvalidElementId)
                {
                    XDBManager.GetInstance().eleGrometry.Add(geometrySymbolId.IntegerValue, elementId.IntegerValue);
                    sw.WriteLine("----------------OnElementEnd,ElementId:" + elementId.IntegerValue);
                    sw.WriteLine("----------------OnElementEnd,顶点个数:" + geometryTemp.vertices.Count);
                    sw.WriteLine("----------------OnElementEnd,面的个数:" + geometryTemp.verIndex.Count / 3);
                }
                XDBManager.GetInstance().AddGeometry(geometry);
            }

            //创建构件
            Proxy proxy = new Proxy();
            proxy.Id = curElement.Id.IntegerValue;
            proxy.guid = curElement.UniqueId;
            proxy.name = curElement.Name;
            proxy.transformer = transfromJson;
            proxy.domain = HWModel.HWXDB.Domain.ARCHI;
            proxy.componentCategory = "Other";
            XDBManager.GetInstance().Proxys.Add(proxy);

            //处理构件几何关联关系
            HWModel.HWXDB.GraphicElementGeometryRelation relation = new HWModel.HWXDB.GraphicElementGeometryRelation();
            relation.Id = elementId.IntegerValue;
            relation.geometryId = geometryId;
            relation.graphicElementId = curElement.Id.IntegerValue;
            XDBManager.GetInstance().GraphicElementGeometryRelations.Add(relation);

            //处理构件和楼层关系
            HWModel.HWXDB.StoreyGraphicElementRelation storeyGraRelaiton = new StoreyGraphicElementRelation();
            storeyGraRelaiton.Id = elementId.IntegerValue;
            storeyGraRelaiton.storeyId = 1;
            storeyGraRelaiton.graphicElementId = curElement.Id.IntegerValue;
            XDBManager.GetInstance().StoreyGraphicElementRelations.Add(storeyGraRelaiton);

            curElement = null;
        }

        public RenderNodeAction OnFaceBegin(FaceNode node)
        {
            
            //sw.WriteLine("OnFaceBegin,NodeName:" + node.NodeName);
            return RenderNodeAction.Proceed;
        }

        public void OnFaceEnd(FaceNode node)
        {

            //sw.WriteLine("OnFaceEnd,NodeName:" + node.NodeName);
        }

        public RenderNodeAction OnInstanceBegin(InstanceNode node)
        {
            var trans = node.GetTransform();
            
            sw.WriteLine(FromatTransform(trans));

            //塞入当前的转换矩阵
            transforms.Push(transforms.Peek().Multiply(node.GetTransform()));
            
            sw.WriteLine("OnInstanceBegin,NodeName:" + node.NodeName);
            return RenderNodeAction.Proceed;
        }

        public void OnInstanceEnd(InstanceNode node)
        {
            transforms.Pop();
            //sw.WriteLine("InstanceNode,NodeName:" + node.NodeName);
        }

        public void OnLight(LightNode node)
        {
            //sw.WriteLine("OnLight,NodeName:" + node.NodeName);
        }

        public RenderNodeAction OnLinkBegin(LinkNode node)
        {
            //sw.WriteLine("OnLinkBegin,NodeName:" + node.NodeName);
            return RenderNodeAction.Proceed;
        }

        public void OnLinkEnd(LinkNode node)
        {
            //sw.WriteLine("OnLinkEnd,NodeName:" + node.NodeName);
        }

        public void OnMaterial(MaterialNode node)
        {
            //sw.WriteLine("OnMaterial,NodeName:" + node.NodeName);
        }

        public void OnPolymesh(PolymeshTopology node)
        {
            var points = node.GetPoints();

            foreach(PolymeshFacet facet in node.GetFacets())
            {
                XYZ xyz = transforms.Peek().OfPoint(points[facet.V1]);
                geometryTemp.verIndex.Add(geometryTemp.vertices.Add(xyz));
                XYZ xyz2 = transforms.Peek().OfPoint(points[facet.V2]);
                geometryTemp.verIndex.Add(geometryTemp.vertices.Add(xyz2));
                XYZ xyz3 = transforms.Peek().OfPoint(points[facet.V3]);
                geometryTemp.verIndex.Add(geometryTemp.vertices.Add(xyz3));

                XYZ v12 = xyz2 - xyz;
                XYZ v13 = xyz3 - xyz;
                XYZ normal = v12.CrossProduct(v13).Normalize();
                int normalIndex = geometryTemp.normals.Add(normal);
                geometryTemp.normalIndex.Add(normalIndex);
                geometryTemp.normalIndex.Add(normalIndex);
                geometryTemp.normalIndex.Add(normalIndex);
            }

            //sw.WriteLine("OnPolymesh");
        }

        public void OnRPC(RPCNode node)
        {
            sw.WriteLine("OnRPC,NodeName:"+node.NodeName);
        }

        public RenderNodeAction OnViewBegin(ViewNode node)
        {
            node.LevelOfDetail = 1;
            sw.WriteLine("OnViewBegin,NodeName:" + node.NodeName);
            return RenderNodeAction.Proceed;
        }

        public void OnViewEnd(ElementId elementId)
        {
            sw.WriteLine("OnViewEnd,ElementId:" + elementId.IntegerValue);
        }

        public bool Start()
        {
            transforms.Push(Transform.Identity);
            sw.WriteLine("******************Start********************");
            return true;
        }
    }
}

 

这个是好用的至少是至今发现上最好用最实用,也是参考然后大改过的 比如解决了Node、纹理丢失、压缩等等问题的项目 主要是Revit二次开发的插件,其中主要是基于Revit2018进行的,实现从Revit中把建筑模型导出来,使用的是GLTF格式的编码。(这个是好用的至少是至今发现上最好用最实用,也是参考然后大改过的 比如解决了Node、纹理丢失、压缩等等问题的项目) 运行操作: 直接双击运行插件,然后选择需要导出的格式与路径,导出格式包括gltf 与glb二进制这些; 运行成功还有返回的信息,导出的格式以及文件等等; 主要使用了RevitAPI.dll 与RevitAPIUI.dll 来Revit二次开发,其中 : //add-in manger 只读模式 使用了SharpGLTF库,SharpGLTF是一个100%.NET标准库,旨在支持Khronos Group glTF 2.0文件格式。 所以使用SharpGLTF生成gltf、glb数据; 该库分为两个主要软件包: SharpGLTF.Core提供读/写文件支持,以及对glTF模型的低级别访问。 SharpGLTF.Toolkit提供了方便的实用程序来帮助创建,操纵和评估glTF模型。 simple example gltf保存为glb格式: var model = SharpGLTF.Schema2.ModelRoot.Load("model.gltf"); model.SaveGLB("model.glb"); IExportContext接口在数据导出中,执行如下的顺序: 将revit的数据解析为我们自己的数据需要继承重写IExportContext就能revit文件进行数据导出和数据转换; * 接口在数据导出中,无链接模型执行如下的顺序: * Start -> OnViewBegin -> onElementBegin -> OnInstanceBegin ->OnMaterial ->OnLight * ->OnFaceBegin OnPolymesh -> OnFaceEnd -> OnInstanceEnd-> OnElementEnd * ->OnViewEnd ->IsCanceled ->Finish、 * 假如有链接模型在执行完非链接的OnElementBegin以后,执行OnLinkBegin,然后执行链接模型里的OnElementBegin……依次类推 依赖环境:Autodesk.RevitAPi Autodesk.Revit.UI 安装nodejs 使用工具:使用npm 安装gltf-pipeline配置系统环境
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值