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;
}
}
}