前言
这个例子介绍如何直接在 Revit 的画布里面绘制任意用户想要的三维图形。或者说,Revit API 暴露了让用户直接参与生成渲染数据的接口。
内容
程序运行的效果如下,它的逻辑:
- 选择一个或多个构件,比如墙,点击完成
- 通过类
DirectContext3D::DrawContext
在画布绘制选中构件
注意:不是所有构件都能被绘制出来,比如门窗,原因在于这个例子没有考虑这种情况,用户可以自己修改代码来实现。另外,这个只是示例,用户完全可以自己生成完全无关的几何图形。Revit 通过这个接口,把图形的生成权力交给了用户。
实现这个逻辑的关键点
实现这个功能的两个关键点:
- 为接口
IDirectContext3DServer
做一个实现,处理绘制相关的内容 - 把接口注册到 Revit Application
IDirectContext3DServer
接口的内容:
namespace Autodesk.Revit.DB.DirectContext3D
{
// DirectContext3D 是一个 external service.
public interface IDirectContext3DServer : ExternalService.IExternalServer
{
// 用于判断在哪些视图下运行。
bool CanExecute(View dBView);
string GetApplicationId();
// 提供包围盒的接口
Outline GetBoundingBox(View dBView);
string GetSourceId();
// 渲染的接口,用于在视图中去显示三维图形
void RenderScene(View dBView, DisplayStyle displayStyle);
bool UseInTransparentPass(View dBView);
// 当前对于第三方插件只能返回false
bool UsesHandles();
}
}
这里面渲染的入口是 RenderScene
,而这个接口的实现里需要调用DrawContext::FlushBuffer
来向渲染管线里面输入内容,注意这是一个static class
。
namespace Autodesk.Revit.DB.DirectContext3D
{
public static class DrawContext
{
// 往图形缓冲区传入内容,包括顶点信息和索引信息,以及顶点是什么类型,三角形、边或者点。
public static void FlushBuffer(VertexBuffer vertexBuffer, int vertexCount, IndexBuffer indexBuffer, int indexCount, VertexFormat vertexFormat, EffectInstance effectInstance, PrimitiveType primitiveType, int start, int primitiveCount);
public static Camera GetCamera();
// 获取一系列剪切面。
public static IList<ClipPlane> GetClipPlanes();
// 获取裁剪区域。
public static Rectangle GetClipRectangle();
//
// 摘要:
// Returns override color that will be applied to geometry during rendering.
public static bool GetOverrideColor(out Color color);
//
// 摘要:
// Returns override transparency that will be applied to geometry during rendering.
public static bool GetOverrideTransparency(out double transparency);
//
// 摘要:
// Gets the rectangle that represents the extent (in 2D) of the Revit view where
// rendering takes place.
public static Rectangle GetViewRectangle();
//
// 摘要:
// Checks whether the facilities of this class are available for use in the current
// scope.
public static bool IsAvailable();
//
// 摘要:
// Checks whether the current rendering pass has been interrupted.
public static bool IsInterrupted();
//
// 摘要:
// Determines whether the current rendering pass is for transparent objects.
public static bool IsTransparentPass();
//
// 摘要:
// Sets the world transformation that will be applied to geometry during rendering.
public static void SetWorldTransform(Transform trf);
}
}
注册用户自己实现的 RevitElementDrawingServer
通过静态方法 ExternalServiceRegistry.GetService
得到对应的 Service,然后将自己的实现加入。
ExternalService directContext3DService = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService);
RevitElementDrawingServer revitServer = new RevitElementDrawingServer(uidoc, elem, m_offset);
directContext3DService.AddServer(revitServer);
从 ExternalServices.BuiltInExternalServices 这个类可以找到目前 API 可以看到的一些服务,应该都有各自的用途:
namespace Autodesk.Revit.DB.ExternalService
{
public static class ExternalServices
{
public static class BuiltInExternalServices
{
public static ExternalServiceId StructuralSectionsService { get; }
public static ExternalServiceId CloudExternalService { get; }
public static ExternalServiceId CodeCheckingParameterService { get; }
public static ExternalServiceId ConnectionTypeChangedService { get; }
public static ExternalServiceId DirectContext3DService { get; }
public static ExternalServiceId DuctFittingAndAccessoryPressureDropService { get; }
public static ExternalServiceId DuctFittingAndAccessoryPressureDropUIService { get; }
public static ExternalServiceId DuctPressureDropService { get; }
public static ExternalServiceId EntitlementExternalService { get; }
public static ExternalServiceId ExternalResourceService { get; }
public static ExternalServiceId ExternalResourceUIService { get; }
public static ExternalServiceId FramingProfileService { get; }
public static ExternalServiceId HoleDefinitionService { get; }
public static ExternalServiceId IFCExporterService { get; }
public static ExternalServiceId IFCImporterService { get; }
public static ExternalServiceId MemberForcesService { get; }
public static ExternalServiceId ModifyConnectionParametersService { get; }
public static ExternalServiceId NavisworksExporterService { get; }
public static ExternalServiceId PathOfTravelCalculationService { get; }
public static ExternalServiceId PipeFittingAndAccessoryPressureDropService { get; }
public static ExternalServiceId PipeFittingAndAccessoryPressureDropUIService { get; }
public static ExternalServiceId PipeFrictionFactorService { get; }
public static ExternalServiceId PipePlumbingFixtureFlowService { get; }
public static ExternalServiceId PipePressureDropService { get; }
public static ExternalServiceId RebarUpdateService { get; }
public static ExternalServiceId SiteInsertService { get; }
public static ExternalServiceId SiteLinkerUIService { get; }
public static ExternalServiceId SnappingService { get; }
public static ExternalServiceId AlignmentService { get; }
public static ExternalServiceId ATFTranslationService { get; }
}
}
}
实现的具体逻辑
RevitElementDrawingServer
实现接口 IDirectContext3DServer
:
Revit 2021 SDK\Samples\DuplicateGraphics\CS\RevitElementDrawingServer.cs
对于如何从 Revit 的 Element 里面获取几何图形:
GeometryElement geomElem = m_element.get_Geometry(options);
List<Solid> allSolids = new List<Solid>();
foreach (GeometryObject geomObj in geomElem)
{
if (geomObj is Solid)
{
Solid solid = (Solid)geomObj;
if (solid.Volume > 1e-06)
allSolids.Add(solid);
}
}
foreach (Solid solid in allSolids)
{
foreach (Face face in solid.Faces)
{
if (face.Area > 1e-06)
{
Mesh mesh = face.Triangulate();
// 处理 face 的三角面片,即离散化数据,这个例子是加入到渲染的 buffer 里面,DrawContext.FlushBuffer
}
}
}
foreach (Edge edge in solid.Edges)
{
IList<XYZ> xyzs = edge.Tessellate();
// 处理 edge 的离散化数据,这个例子是加入到渲染的 buffer 里面,DrawContext.FlushBuffer
}