First room in 3D, great!

the new lookup table is out…

is it possible to use this geometry representation from LookUpTable…

Does anyone know how it works … just one snippet ?

KR

Andreas

I can’t give a snippet, but a few pages removed from the link you provided is this page, which I believe is the full source: RevitLookup/source/RevitLookup/Core/Visualization at dev · jeremytammik/RevitLookup · GitHub

1 Like

@jacob.small

in this snippets i can`t find anything

i don`t how to “decode” that.

Context? Which file is that in the repository section I sent?

Note that this is NOT an easy implementation.

1 Like

@jacob.small

i looked just in one, but here i zipped all
Geometry.7z (87.6 KB)

Your IDE or Git process is likely part of the problem. Look at one of the .cs files the github page which I linked using your browser.

1 Like

@jacob.small

you are right, just double click, the code is readable

1 Like

@jacob.small

OK, but from that point, how can i combine that with Revit API. So the lookup-Table is accessing Revit itself, can i based on that… create geometry via python (like chatGPT is “cheating” me)

import clr
import System
from System import Guid
from System.Collections.Generic import List
from Autodesk.Revit.DB import Solid, View, DisplayStyle, Outline, BoundingBoxXYZ
from Autodesk.Revit.DB.ExternalService import ExternalServiceRegistry, ExternalServices
from Autodesk.Revit.DB.DirectContext3D import IDirectContext3DServer, DrawContext, RenderingBufferStorage
from Autodesk.Revit.UI import UIApplication

class SolidVisualizationServer(IDirectContext3DServer):
    def __init__(self):
        self._solid = None
        self._has_effects_updates = True
        self._has_geometry_updates = True
        self._guid = Guid.NewGuid()
        self._render_lock = System.Object()
        self._face_buffers = List[RenderingBufferStorage]()
        self._edge_buffers = List[RenderingBufferStorage]()
        self._transparency = 0.0
        self._scale = 1.0
        self._face_color = None
        self._edge_color = None
        self._draw_face = False
        self._draw_edge = False

    def GetServerId(self):
        return self._guid

    def GetVendorId(self):
        return "RevitLookup"

    def GetName(self):
        return "Solid visualization server"

    def GetDescription(self):
        return "Solid geometry visualization"

    def GetServiceId(self):
        return ExternalServices.BuiltInExternalServices.DirectContext3DService

    def GetApplicationId(self):
        return ""

    def GetSourceId(self):
        return ""

    def UsesHandles(self):
        return False

    def CanExecute(self, view):
        return True

    def UseInTransparentPass(self, view):
        return self._draw_face and self._transparency > 0

    def GetBoundingBox(self, view):
        bounding_box = self._solid.GetBoundingBox()
        min_point = bounding_box.Transform.OfPoint(bounding_box.Min)
        max_point = bounding_box.Transform.OfPoint(bounding_box.Max)
        return Outline(min_point, max_point)

    def RenderScene(self, view, display_style):
        with self._render_lock:
            try:
                if self._has_geometry_updates:
                    self._map_geometry_buffer()
                    self._has_geometry_updates = False

                if self._has_effects_updates:
                    self._update_effects()
                    self._has_effects_updates = False

                if self._draw_face:
                    is_transparent_pass = DrawContext.IsTransparentPass()
                    if (is_transparent_pass and self._transparency > 0) or (not is_transparent_pass and self._transparency == 0):
                        for buffer in self._face_buffers:
                            DrawContext.FlushBuffer(buffer.VertexBuffer,
                                                    buffer.VertexBufferCount,
                                                    buffer.IndexBuffer,
                                                    buffer.IndexBufferCount,
                                                    buffer.VertexFormat,
                                                    buffer.EffectInstance, PrimitiveType.TriangleList, 0,
                                                    buffer.PrimitiveCount)

                if self._draw_edge:
                    for buffer in self._edge_buffers:
                        DrawContext.FlushBuffer(buffer.VertexBuffer,
                                                buffer.VertexBufferCount,
                                                buffer.IndexBuffer,
                                                buffer.IndexBufferCount,
                                                buffer.VertexFormat,
                                                buffer.EffectInstance, PrimitiveType.LineList, 0,
                                                buffer.PrimitiveCount)
            except Exception as e:
                self.RenderFailed(self, e)

    def _map_geometry_buffer(self):
        scaled_solid = RenderGeometryHelper.ScaleSolid(self._solid, self._scale)
        face_index = 0
        for face in scaled_solid.Faces:
            buffer = self._create_or_update_buffer(self._face_buffers, face_index)
            self._map_face_buffers(buffer, face)
            face_index += 1

        edge_index = 0
        for edge in scaled_solid.Edges:
            buffer = self._create_or_update_buffer(self._edge_buffers, edge_index)
            self._map_edge_buffers(buffer, edge)
            edge_index += 1

    def _map_face_buffers(self, buffer, face):
        mesh = face.Triangulate()
        RenderHelper.MapSurfaceBuffer(buffer, mesh, 0)

    def _map_edge_buffers(self, buffer, edge):
        mesh = edge.Tessellate()
        RenderHelper.MapCurveBuffer(buffer, mesh)

    def _create_or_update_buffer(self, buffers, index):
        if len(buffers) > index:
            return buffers[index]
        else:
            buffer = RenderingBufferStorage()
            buffers.Add(buffer)
            return buffer

    def _update_effects(self):
        for buffer in self._face_buffers:
            if not buffer.EffectInstance:
                buffer.EffectInstance = EffectInstance(buffer.FormatBits)
            buffer.EffectInstance.SetColor(self._face_color)
            buffer.EffectInstance.SetTransparency(self._transparency)

        for buffer in self._edge_buffers:
            if not buffer.EffectInstance:
                buffer.EffectInstance = EffectInstance(buffer.FormatBits)
            buffer.EffectInstance.SetColor(self._edge_color)

    def UpdateFaceColor(self, color):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        with self._render_lock:
            self._face_color = color
            self._has_effects_updates = True
            ui_document.UpdateAllOpenViews()

    def UpdateEdgeColor(self, color):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        with self._render_lock:
            self._edge_color = color
            self._has_effects_updates = True
            ui_document.UpdateAllOpenViews()

    def UpdateTransparency(self, value):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        with self._render_lock:
            self._transparency = value
            self._has_effects_updates = True
            ui_document.UpdateAllOpenViews()

    def UpdateScale(self, value):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        self._scale = value
        with self._render_lock:
            self._has_geometry_updates = True
            self._has_effects_updates = True
            self._face_buffers.Clear()
            self._edge_buffers.Clear()
            ui_document.UpdateAllOpenViews()

    def UpdateFaceVisibility(self, value):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        with self._render_lock:
            self._draw_face = value
            ui_document.UpdateAllOpenViews()

    def UpdateEdgeVisibility(self, value):
        ui_document = UIApplication.ActiveUIDocument
        if ui_document is None:
            return

        with self._render_lock:
            self._draw_edge = value
            ui_document.UpdateAllOpenViews()

    def Register(self, solid):
        self._solid = solid
        Application.ActionEventHandler.Raise(lambda application: self._register_internal(application))

    def _register_internal(self, application):
        if application.ActiveUIDocument is None:
            return

        direct_context_service = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService)
        server_ids = direct_context_service.GetActiveServerIds()
        direct_context_service.AddServer(self)
        server_ids.Add(self.GetServerId())
        direct_context_service.SetActiveServers(server_ids)
        application.ActiveUIDocument.UpdateAllOpenViews()

    def Unregister(self):
        Application.ActionEventHandler.Raise(lambda application: self._unregister_internal(application))

    def _unregister_internal(self, application):
        direct_context_service = ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService)
        direct_context_service.RemoveServer(self.GetServerId())
        if application.ActiveUIDocument:
            application.ActiveUIDocument.UpdateAllOpenViews()

    def RenderFailed(self, sender, args):
        print(f"Render failed: {args.Exception}")

# Usage example:
# solid_visualization_server = SolidVisualizationServer()
# solid_visualization_server.Register(some_solid_instance)

Yes you can, but I am curious to know the ‘why’ behind this. What is your end goal? Personally Inwouldnt try to recreate via Python as it won’t be as stable or functional as what you’d get with C# due to the nature of the graphics server services.

@jacob.small

the goal is to create geometry with this. because dynamo geometry is unstable. Sometimes revit collabses… at least i have in everrage 800MB project. Aim when it works, i will migrate to PyRevit.

No it isn’t. It’s actually orders of magnitude more stable and capable than Revit’s geometry engine. Dynamo uses the same engine as AutoCAD and many other core Autodesk tools - Revit is the outlier actually. This means that the geometry engine for Dynamo geometry has a LOT more users than that for Revit who are building stuff much more complex than what is done in Revit. As an example remember the minimum curve length and maximum geometry size - AutoCAD doesn’t have that - you can draw a microchip on the moon at scale if you wanted to.

The issues we see with geometry is usually in the translation from Revit geometry to Dynamo and vice versa - similar to how geometry imports work today - sometimes those SAT imports just aren’t right… Hopefully that clarifies things enough that we can move past knocking the Dynamo geometry engine now and get to what you are after.

Knowing that is your goal, and you’ll need to look into other tools rather than this one as this is for viewing geometry rather than the elements (what Revit typically displays). It currently does so by extracting existing geometry.

To create geometry you need to use the correct class in the Revit API, and this class will vary depending on what type of geometry you want to make. This is a partial listing:

  • Point = XYZ class,
  • Line = Line class
  • Arc = Arc class
  • Nurbs curve = NurbsSpline
  • Surface = BRepBuilder
  • Solid = BRepBuilder
  • Mesh = TessellatedShapeBuilder

I believe that you’re familiar with the basic curve creation methods already, but if not start there. For the higher level complexities look into the shape builder as a starting point.
Some notes before you progress:

  1. This is not for the faint of heart - you’re going to have to spend a LOT of time and work out all the great many edge cases to generate geometry from scratch. Plan on some basic geometry and math education as a start point, as you won’t be able to just say ‘give me a cube’ as that doesn’t exist in Revit. And when it gets to more complex stuff (surface-surface intersection) there isn’t really a single quick algorithm in existence to do the work - just a few basic ones to ‘cover most cases’ followed by a MASSIVE set of edge cases to cover.
  2. I don’t recommend building it with Python as the Dynamo for Revit team hasn’t necessarily built all the necessary wrappers to access everything you’ll need (think of the iFamilyLoadOptions issues you’re familiar with). This means you’ll be writing a Python conversion which is a VERY big lift and should result in a PR tot he Dynamo for Revit project so your efforts can help the larger community.
  3. Remember that if/when you do push this content into Revit as an element it will not be editable; as such you may want to look into creating elements using the family authoring tools (which I feel are a bit more accessible to Revit users).
  4. As a productive ‘entry’ tool you could look into building your own Dynamo Geometry > Revit Geometry conversion tool. The source code for the ToProtoType is available on the GitHub and you can learn how to convert Dynamo solids into Revit solids and then generate families with that shape, no import needed. I do recommend C# for that, but Python can be made to fit at the prototype phase.
3 Likes

Hi,

If it’s just for visualization, as an alternative to DirectContext3D, you can use WPF with the HelixToolkit library (OOTB with Dynamo)

here I’m also using the Dynamo MeshToolKit package to simplify conversion

code (Revit 2025 + IronPython3 with WPF)

import clr
import sys
import System
clr.AddReference("System.Numerics")

clr.AddReference('HelixToolkit')
clr.AddReference('SharpDX.Mathematics')
clr.AddReference('HelixToolkit.Core.Wpf')
clr.AddReference('HelixToolkit.SharpDX.Core')
clr.AddReference('HelixToolkit.SharpDX.Core.Wpf')
from HelixToolkit import *
from HelixToolkit.Wpf.SharpDX import *
from HelixToolkit.SharpDX.Core import *
from SharpDX import *
import SharpDX
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS


clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
clr.AddReference("IronPython.Wpf")
clr.AddReference('System.Core')
clr.AddReference('System.Xml')
clr.AddReference('PresentationCore')
clr.AddReference('PresentationFramework')
clr.AddReferenceByPartialName("WindowsBase")

    
from System.IO import StringReader
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application

try:
    import wpf
    import time
except ImportError:
    wpf = None


class MeshViewer(Window):
    LAYOUT = '''
    <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:hx="http://helix-toolkit.org/wpf"
        Title="..."
        Height="700"
        Width="600">
        <Grid>
        <Viewbox Grid.Row="0">
            <hx:HelixViewport3D 
                Height="400" 
                Width="400" 
                VerticalAlignment="Bottom" 
                ZoomExtentsWhenLoaded="True"  
                CameraRotationMode="Trackball" 
                IsViewCubeEdgeClicksEnabled="True" 
                ZoomAroundMouseDownPoint="True" 
                 Margin="5,35,5,2">
                <hx:DefaultLights/>
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <GeometryModel3D x:Name="model3D">
                            <GeometryModel3D.Geometry >
                                <MeshGeometry3D x:Name="meshMain">
                                </MeshGeometry3D>
                            </GeometryModel3D.Geometry>
                             <GeometryModel3D.Material>
                             <DiffuseMaterial x:Name="matDiffuseMain">
                                    <DiffuseMaterial.Brush>
                                        <SolidColorBrush Color="Gray"/>
                                    </DiffuseMaterial.Brush>
                                </DiffuseMaterial>
                        </GeometryModel3D.Material>
                        </GeometryModel3D>
                    </ModelVisual3D.Content>
                </ModelVisual3D>
                <hx:GridLinesVisual3D Width="8" Length="8" MinorDistance="1" MajorDistance="1" Thickness="0.01"/>
            </hx:HelixViewport3D >
        </Viewbox>
        </Grid>
    </Window>
    '''
    
    def __init__(self, mesh):
        self.ui = wpf.LoadComponent(self, StringReader(MeshViewer.LAYOUT))
        self.mesh = mesh
        self.Title = "3D Viewer"
        indices = System.Windows.Media.Int32Collection([System.Int32(i) for i in mesh.VertexIndicesByTri()])
        positions = System.Windows.Media.Media3D.Point3DCollection([System.Windows.Media.Media3D.Point3D(p.X, p.Y, p.Z) for p in mesh.Vertices()])
        self.meshMain.TriangleIndices = indices
        self.meshMain.Positions = positions
        
if wpf is not None:
    input_mesh = IN[0]
    mesh_viewer = MeshViewer(input_mesh)
    mesh_viewer.Show()
3 Likes

@c.poupin

thank you for pushing the topic… so it is realy a alternative for directShape.

@c.poupin can confirm, but I don’t see it generating any elements in the Revit model; it is displaying the content in this case.

If the goal is to make new model elements from existing geometry without using a direct shape you can quarry them, and generate a new family with the relevant geometry included, and then load an instance of that family.

2 Likes

an alternative to DirectContext3D API (not DirectShape)

you can find information about DirectContext3D API here (which is used here in RevitLookUp to visualize geometries.)

1 Like

@c.poupin ,

can i access that also outside from dynamo. Only with python and driven by PyRevit ?

  • DirectContext3D API → can access outside from dynamo
  • HelixToolkit → you need to import the HelixToolkit library
    (but maybe pyRevit already uses it, I don’t know)

https://www.nuget.org/packages?q=helixtoolkit&frameworks=net%2Cnetcoreapp%2Cnetstandard%2Cnetframework&includeComputedFrameworks=true&frameworkFilterMode=all&prerel=false&sortby=relevance

1 Like

@c.poupin

i installed meshToolkid, Ironpython2 and 3… is there still something missing
Py3DShape.dyn (15.4 KB)

@Draxl_Andreas Select the good python engine ? And you use which version of Dynamo ?

1 Like

@c.poupin ,

i was not switching to IronPython


thats great! :tada::confetti_ball::dart:

lets see how to migrate to pyRevit, challanges:

  • Element.Geometry
  • Meshes
1 Like