Sunday, February 16, 2014

Rhino 3dm File Load / Draw / Bake

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:

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()

No comments:

Post a Comment