What is DirectContext3D in Revit?

DirectContext3D (DC3D) is Revit’s low-level graphics hook. It lets your add-in inject GPU-ready geometry directly into the view pipeline (coexisting with native model graphics) without creating or modifying Revit elements.
You implement a small server object, Revit calls you during its render passes, and you submit buffers containing points/lines/triangles.
Think “mini graphics API inside Revit,” not “element creation.”

Why DC3D instead of DirectShape, AVF, or imports?
The first question that may arise is why using Direct Context 3D when there are so many formats that can be imported into Revit like Direct Shape, CAD, etc.
- No document footprint. Nothing is added to the model; you’re just drawing. Great for previews, diagnostics, or visualizations sourced from external systems.
- Performance. You feed vertex/index buffers directly to Revit’s renderer; no topology conversion to Revit’s element schema. This avoids the import/translation cost.
- Workflow fit. Autodesk itself uses DC3D for Coordination Model (Navisworks display inside Revit) and in Dynamo when it previsualizes geometry, canonical examples of streaming external content.
Use DirectShape when you need persistent, schedulable, selectable model geometry.
Use AVF/TemporaryGraphics for high-level color-by-analysis overlays bound to elements.
Use DC3D for everything that looks like a viewer, live preview, large external meshes, custom diagnostics, or lightweight “heads-up” graphics.

Architecture: the pieces you implement
You register a server that implements Autodesk.Revit.DB.DirectContext3D.IDirectContext3DServer
.
Revit calls your server on each redraw, in both opaque and transparent passes.
At that point you can inspect the camera, clip planes, and overrides, set a world transform, and flush buffers to draw.
Key methods you’ll implement:
GetName()
,GetDescription()
,GetServerId()
,GetVendorId()
,GetServiceId()
,GetSourceId()
– identity and registration.RenderScene(View, DisplayStyle)
– your main callback to submit geometry.UseInTransparentPass()
– opt into the transparent pass if you draw translucent things.UsesHandles()
– return false for third-party add-ins (handle elements are for internal use).
You register with the built-in external service:
public Result OnStartup(UIControlledApplication a)
{
var svcId = ExternalServices.BuiltInExternalServices.DirectContext3DService;
var svc = ExternalServiceRegistry.GetService(svcId) as MultiServerService;
var myServer = new MyDc3dServer();
svc.AddServer(myServer);
var actives = svc.GetActiveServerIds();
if (!actives.Contains(myServer.GetServerId())) svc.SetActiveServers(actives.Append(myServer.GetServerId()).ToList());
return Result.Succeeded;
}
The service identity and registration flow are part of the External Services framework.
The render pipeline you participate in
Inside RenderScene
, you:
- Build or reuse buffers
VertexBuffer
(capacity in floats) + a typed stream (VertexStreamPosition[Normal][Colored]
)IndexBuffer
(capacity in shorts) + a typed stream (IndexStreamPoint/Line/Triangle
)- A
VertexFormat
matching your vertex layout (e.g.,PositionColored
). - An
EffectInstance
describing shading (ambient/diffuse/specular, transparency) when colors aren’t per-vertex.
- Fill the buffers
- Map → write with the typed stream → unmap.
- Use
VertexPositionColored.GetSizeInFloats()
(or the relevant type) to size yourVertexBuffer
.
- Submit the draw
DrawContext.FlushBuffer(vertexBuffer, vertexCount, indexBuffer, indexCount, vertexFormat, effectInstance, primitiveType, start, primitiveCount);
- Revit may invalidate buffers across device resets; check
IsValid()
and recreate if needed.
- Respond to view state
- Read camera, clip rectangle, and section-box clip planes via
DrawContext.GetCamera/GetClipPlanes/GetClipRectangle
. - Honor
DrawContext.IsTransparentPass()
andGetOverrideColor/Transparency()
to match Revit’s overrides. - Use
DrawContext.SetWorldTransform()
for positioning without touching your vertex data.
- Read camera, clip rectangle, and section-box clip planes via
Minimal, correct example (colored triangle)
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.DirectContext3D;
using System;
using System.Collections.Generic;
public class MyDc3dServer : IDirectContext3DServer
{
readonly Guid _id = new Guid("A2E1D2B4-3A2D-4F1F-9F10-92F8A8B0AA01");
public string GetName() => "Triangle Demo";
public string GetDescription() => "Minimal DirectContext3D sample";
public Guid GetServerId() => _id;
public string GetVendorId() => "E-VRS";
public string GetSourceId() => "TriangleDemo";
public Guid GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService;
public bool UseInTransparentPass() => false; // change to true for translucent
public bool UsesHandles() => false; // third-party add-ins should return false
public void RenderScene(View view, DisplayStyle displayStyle)
{
// 1) Build vertex buffer (3 vertices, Position+Color)
int floatsPerVertex = VertexPositionColored.GetSizeInFloats(); // from API
using var vbuf = new VertexBuffer(3 * floatsPerVertex);
vbuf.Map(0);
var vstream = vbuf.GetVertexStreamPositionColored();
var c = new ColorWithTransparency(200, 80, 60, 0); // opaque
vstream.AddVertex(new VertexPositionColored(new XYZ(0, 0, 0), c));
vstream.AddVertex(new VertexPositionColored(new XYZ(5, 0, 0), c));
vstream.AddVertex(new VertexPositionColored(new XYZ(0, 5, 0), c));
vbuf.Unmap();
// 2) Build index buffer (one triangle)
using var ibuf = new IndexBuffer(3);
ibuf.Map(0);
var istream = ibuf.GetIndexStreamTriangle();
istream.AddTriangle(new IndexTriangle(0, 1, 2));
ibuf.Unmap();
// 3) Format + effect (color comes from vertices)
using var vf = new VertexFormat((int)VertexFormatBits.PositionColored);
using var fx = new EffectInstance(vf);
// 4) Submit draw
DrawContext.FlushBuffer(
vbuf, 3, ibuf, 3, vf, fx,
PrimitiveType.TriangleList, 0, 1
);
}
}
This skeleton matches the DC3D buffer model (typed vertex/index streams, vertex format, effect instance, flush).
The capacity semantics and flush signature follow the official docs.
Performance tips (what actually matters)
- Reuse GPU buffers across frames and only rebuild when inputs change. Always check
IsValid()
before reuse (device resets can invalidate). - Stream big scenes incrementally. If
DrawContext.IsInterrupted()
flips, bail early and continue next frame to keep the UI responsive. - Right-size buffers. Use
VertexPosition[Normal][Colored].GetSizeInFloats()
to allocate exact capacities. - Prefer indexed primitives to cut vertex bandwidth.
- Scope by view state. Honor clip planes and render passes; don’t draw what the user can’t see.
- Scene size caveat. Extremely large scenes may need culling/tiling strategies; several community threads discuss degradation thresholds.

Real-world use cases
- External model viewers inside Revit: stream meshes from Navisworks/IFC/GLTF without import (Coordination Model-style).
- Analysis overlays: vectors, heatmaps, isolines derived from simulation data—no temp elements required. (See community examples coloring faces/triangles.)
- Live jigs & previews: loft previews, rebar cages, fabrication nest visualizers—update on mouse move without touching the doc.
- Point-cloud post-effects & sparse diagnostics: quickly draw millions of markers/lines customized to your tools.

Limitations and gotchas
- Not persistent or selectable. Nothing goes into schedules; nothing participates in snapping/joins. DC3D is draw-only.
- Depth & order. You can’t disable depth testing; use the transparent pass and geometry sorting to avoid artifacts for translucent content.
- Handle elements are special.
UsesHandles()
should be false for external add-ins; Autodesk reserves handle-based flows for internal features. - API invalidation. Revit can invalidate DC3D objects between frames; be prepared to recreate buffers, formats, and effects.

Bottom line
If you need fast, transient, and view-aware graphics inside Revit (especially from external data sources) DirectContext3D is the right tool.
It gives you the control of a lightweight graphics API with the context of the current Revit view, camera, and section box, letting you build rich visual experiences without polluting the model. (Revit API Docs, Autodesk)
If you’d like, tell us your exact scenario (analysis overlay, mesh viewer, jig, etc.) and we can sketch a production-ready buffer lifecycle and state cache tailored to it.
Valentin Noves
I'm a versatile leader with broad exposure to projects and procedures and an in-depth understanding of technology services/product development. I have a tremendous passion for working in teams driven to provide remarkable software development services that disrupt the status quo. I am a creative problem solver who is equally comfortable rolling up my sleeves or leading teams with a make-it-happen attitude.