As part of getting an interactive preview into the Super Matter Tools UI I've been developing some proof-of-concept code to read Rhino 3dm files, load the objects into memory (without creating Rhino geometry), and draw them using a fast "display conduit". Further, I've worked on posing those objects (transforming them), and then "baking" them into actual Rhino geometry so they can be used to render animations.
This is similar to how Grasshopper lets users preview geometry and manipulate it without creating Rhino objects. Then, when things are set, baking them.
Here are a few screen captures of the code running (the full source code is listed at the end of this post):
A Kuka KR100HAL90 robot mesh, drawn shaded using the display conduit. The bounding box for the objects is expanded by 5% and drawn as well. For an overview of the Display Conduit in Rhino see this post: Display Conduit Introduction for RhinoCommon.
The material diffuse color is changed to green and drawn again:
The robot is rotated 90 degrees in Z, material is changed, and drawn:
This allows for drawing using the nicer rendering modes available - in this case Artistic:
The full python test code:
import rhinoscriptsyntax as rs
import scriptcontext
import Rhino
import System
import System.Drawing
def Main():
# The 3dm file to open and read
pathname = "C:\\KR100HAL90.3dm"
# Open it and get the objects
drawObjs = Load3DMFile(pathname)
# Make the display conduit object for drawing
dc = DisplayConduit(drawObjs)
# Draw
dc.xform = rs.XformIdentity()
dc.Enabled = True
rs.Redraw()
rs.GetPoint("Click to continue...") # Wait...
# Change the material color and draw again
dc.material.Diffuse = System.Drawing.Color.Green
rs.Redraw()
rs.GetPoint("Click to rotate...") # Wait...
# Rotate (and change color again)
cplane = rs.ViewCPlane()
dc.xform = rs.XformRotation2(90.0, cplane[3], cplane[0])
dc.drawObjsBBox.Transform(dc.xform) # Update the bounding box to the rotated state
dc.material.Diffuse = System.Drawing.Color.Yellow
rs.Redraw()
rs.GetPoint("Click to Bake Meshes...") # Wait
# Convert the mesh to Rhino objects
meshList = BakeMeshes(dc.drawObjs, dc.xform)
if meshList:
print "Meshes created: %d" % len(meshList)
rs.Redraw()
# Stop drawing
dc.Enabled = False
rs.Redraw()
# Read the 3dm file and return a list of the objects within it
def Load3DMFile(pathname):
f3dm = Rhino.FileIO.File3dm.Read(pathname)
if (f3dm):
objs = f3dm.Objects
return objs
# Convert the list of objects passed to Rhino geometry. Then transform
# them with the transformation matrix passed
def BakeMeshes(drawObjs, xform):
meshList = []
for item in drawObjs:
if item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Mesh:
mesh = item.Geometry
verts = mesh.Vertices
faces = mesh.Faces
rhinoMesh = scriptcontext.doc.Objects.AddMesh(mesh)
meshList.append(rhinoMesh)
if (meshList):
rs.TransformObjects(meshList, xform)
return meshList
# Display Conduit class to draw in the viewports
class DisplayConduit(Rhino.Display.DisplayConduit):
def __init__(self, objs):
# Store the objects we are to draw
self.drawObjs = objs
# Store the transformation to apply - initially none
self.xform = Rhino.Geometry.Transform.Identity
# Get the bounding box for all the objects passed
self.drawObjsBBox = Rhino.Geometry.BoundingBox(Rhino.Geometry.Point3d(-1,-1,-1), Rhino.Geometry.Point3d(1,1,1))
for item in self.drawObjs:
bbox = item.Geometry.GetBoundingBox(False)
if (bbox):
self.drawObjsBBox.Union(bbox)
# Expand the bbox by 5% of the diagonal length for use in zoom extents
amount = self.drawObjsBBox.Diagonal.Length*0.05
self.drawObjsBBox.Inflate(amount)
# Init the material to draw with
self.material = Rhino.Display.DisplayMaterial()
self.material.Diffuse = System.Drawing.Color.OrangeRed # Kuka robot color!
self.material.Shine = 0.8
# Called to quickly return the bounding box for the scene including our objects
def CalculateBoundingBox(self, calculateBoundingBoxEventArgs):
calculateBoundingBoxEventArgs.IncludeBoundingBox(self.drawObjsBBox)
# Called to quickly return the zoom extents bounding box including our objects
def CalculateBoundingBoxZoomExtents(self, calculateBoundingBoxEventArgs):
calculateBoundingBoxEventArgs.IncludeBoundingBox(self.drawObjsBBox)
# Called to draw the objects to the viewport. This is done before any Rhino
# objects which will be drawn over ours.
def PreDrawObjects(self, drawEventArgs):
drawEventArgs.Display.DrawBox(self.drawObjsBBox, System.Drawing.Color.White)
drawEventArgs.Display.PushModelTransform(self.xform) # Apply the transform
for item in self.drawObjs:
if item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Curve:
drawEventArgs.Display.DrawCurve(item.Geometry, System.Drawing.Color.Green)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Brep:
drawEventArgs.Display.DrawBrepWires(item.Geometry, System.Drawing.Color.Red)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Mesh:
#drawEventArgs.Display.DrawMeshWires(item.Geometry, System.Drawing.Color.Blue)
drawEventArgs.Display.DrawMeshShaded(item.Geometry, self.material)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Point:
drawEventArgs.Display.DrawPoint(item.Geometry.Location, System.Drawing.Color.White)
drawEventArgs.Display.PopModelTransform() # Remove the transformation
# Run it...
if (__name__ == "__main__"):
Main()
This is similar to how Grasshopper lets users preview geometry and manipulate it without creating Rhino objects. Then, when things are set, baking them.
Here are a few screen captures of the code running (the full source code is listed at the end of this post):
A Kuka KR100HAL90 robot mesh, drawn shaded using the display conduit. The bounding box for the objects is expanded by 5% and drawn as well. For an overview of the Display Conduit in Rhino see this post: Display Conduit Introduction for RhinoCommon.
The material diffuse color is changed to green and drawn again:
The robot is rotated 90 degrees in Z, material is changed, and drawn:
The mesh is "baked", converting them to Rhino objects:
This allows for drawing using the nicer rendering modes available - in this case Artistic:
The full python test code:
import rhinoscriptsyntax as rs
import scriptcontext
import Rhino
import System
import System.Drawing
def Main():
# The 3dm file to open and read
pathname = "C:\\KR100HAL90.3dm"
# Open it and get the objects
drawObjs = Load3DMFile(pathname)
# Make the display conduit object for drawing
dc = DisplayConduit(drawObjs)
# Draw
dc.xform = rs.XformIdentity()
dc.Enabled = True
rs.Redraw()
rs.GetPoint("Click to continue...") # Wait...
# Change the material color and draw again
dc.material.Diffuse = System.Drawing.Color.Green
rs.Redraw()
rs.GetPoint("Click to rotate...") # Wait...
# Rotate (and change color again)
cplane = rs.ViewCPlane()
dc.xform = rs.XformRotation2(90.0, cplane[3], cplane[0])
dc.drawObjsBBox.Transform(dc.xform) # Update the bounding box to the rotated state
dc.material.Diffuse = System.Drawing.Color.Yellow
rs.Redraw()
rs.GetPoint("Click to Bake Meshes...") # Wait
# Convert the mesh to Rhino objects
meshList = BakeMeshes(dc.drawObjs, dc.xform)
if meshList:
print "Meshes created: %d" % len(meshList)
rs.Redraw()
# Stop drawing
dc.Enabled = False
rs.Redraw()
# Read the 3dm file and return a list of the objects within it
def Load3DMFile(pathname):
f3dm = Rhino.FileIO.File3dm.Read(pathname)
if (f3dm):
objs = f3dm.Objects
return objs
# Convert the list of objects passed to Rhino geometry. Then transform
# them with the transformation matrix passed
def BakeMeshes(drawObjs, xform):
meshList = []
for item in drawObjs:
if item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Mesh:
mesh = item.Geometry
verts = mesh.Vertices
faces = mesh.Faces
rhinoMesh = scriptcontext.doc.Objects.AddMesh(mesh)
meshList.append(rhinoMesh)
if (meshList):
rs.TransformObjects(meshList, xform)
return meshList
# Display Conduit class to draw in the viewports
class DisplayConduit(Rhino.Display.DisplayConduit):
def __init__(self, objs):
# Store the objects we are to draw
self.drawObjs = objs
# Store the transformation to apply - initially none
self.xform = Rhino.Geometry.Transform.Identity
# Get the bounding box for all the objects passed
self.drawObjsBBox = Rhino.Geometry.BoundingBox(Rhino.Geometry.Point3d(-1,-1,-1), Rhino.Geometry.Point3d(1,1,1))
for item in self.drawObjs:
bbox = item.Geometry.GetBoundingBox(False)
if (bbox):
self.drawObjsBBox.Union(bbox)
# Expand the bbox by 5% of the diagonal length for use in zoom extents
amount = self.drawObjsBBox.Diagonal.Length*0.05
self.drawObjsBBox.Inflate(amount)
# Init the material to draw with
self.material = Rhino.Display.DisplayMaterial()
self.material.Diffuse = System.Drawing.Color.OrangeRed # Kuka robot color!
self.material.Shine = 0.8
# Called to quickly return the bounding box for the scene including our objects
def CalculateBoundingBox(self, calculateBoundingBoxEventArgs):
calculateBoundingBoxEventArgs.IncludeBoundingBox(self.drawObjsBBox)
# Called to quickly return the zoom extents bounding box including our objects
def CalculateBoundingBoxZoomExtents(self, calculateBoundingBoxEventArgs):
calculateBoundingBoxEventArgs.IncludeBoundingBox(self.drawObjsBBox)
# Called to draw the objects to the viewport. This is done before any Rhino
# objects which will be drawn over ours.
def PreDrawObjects(self, drawEventArgs):
drawEventArgs.Display.DrawBox(self.drawObjsBBox, System.Drawing.Color.White)
drawEventArgs.Display.PushModelTransform(self.xform) # Apply the transform
for item in self.drawObjs:
if item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Curve:
drawEventArgs.Display.DrawCurve(item.Geometry, System.Drawing.Color.Green)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Brep:
drawEventArgs.Display.DrawBrepWires(item.Geometry, System.Drawing.Color.Red)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Mesh:
#drawEventArgs.Display.DrawMeshWires(item.Geometry, System.Drawing.Color.Blue)
drawEventArgs.Display.DrawMeshShaded(item.Geometry, self.material)
elif item.Geometry.ObjectType == Rhino.DocObjects.ObjectType.Point:
drawEventArgs.Display.DrawPoint(item.Geometry.Location, System.Drawing.Color.White)
drawEventArgs.Display.PopModelTransform() # Remove the transformation
# Run it...
if (__name__ == "__main__"):
Main()