Reading 3DM File Objects into Memory

We are drawing some objects using a Display Conduit. It is working well and we call:

def PreDrawObjects(self, drawEventArgs):
    for part in self.machineMeshParts:
        drawEventArgs.Display.DrawObject(part.geometry, part.xform)

Right now the geometry is a mesh object imported from a 3DM file into Rhino. After importing we get it using ObjectsByName().

We’d rather not do that. Instead we’d like to directly read the 3DM file and bring the objects into memory. We don’t want to create Rhino objects first (we draw them ourselves so having Rhino hang on to them is wasteful).

Anyone have thoughts on how we can do this (using RhinoCommon methods or any other).

Thanks,
Mark

You could use the File3dm.Read() function to read a 3dm file into memory and not have it associated with the document. Then you can dig through that class to get at a mesh.
http://4.rhino3d.com/5/rhinocommon/html/M_Rhino_FileIO_File3dm_Read.htm

Thanks, Steve. Works nicely. Here’s a chunk of demo code in case someone else finds this thread:

import Rhino

    def main():
        path = "LeastResistance.3dm"
        f3dm = Rhino.FileIO.File3dm.Read(path)
        if (f3dm):
            objs = f3dm.Objects
            item = FindByName(objs, "HikersChoice")
            if (item):
                print item.Geometry
            
    def FindByName(objs, name):
        for item in objs:
            n = item.Attributes.Name
            if (n == name):
                return item
        return None
    
    main()

Mark

2 Likes

Having some trouble actually drawing the objects I load into memory. Here’s my (as simple as I could make it) test program:

import rhinoscriptsyntax as rs
import scriptcontext
import Rhino
import System
import System.Drawing

def Main():
    # Get the pathname to open
    pathname = "C:\\Users\\Mark Meier\\Desktop\\3DMFileDrawTest\\Test.3dm"
    pathname = pathname.replace("\\", "/")
    print pathname
    # Open it and get the objects
    drawObjs = Load3DMFile(pathname)
    
    # Make the display conduit object for drawing
    dc = DisplayConduit(drawObjs)
    dc.Enabled = True
    rs.Redraw()
    rs.GetPoint("Enter a point") # only way I could think to pause...
    dc.Enabled = False

def Load3DMFile(pathname):
    f3dm = Rhino.FileIO.File3dm.Read(pathname)
    if (f3dm):
        objs = f3dm.Objects
        return objs

class DisplayConduit(Rhino.Display.DisplayConduit):
    def __init__(self, objs):
        # Store the objects we are to draw
        self.drawObjs = objs
        
        # 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)
      
        # Init the material to draw with
        self.material = Rhino.Display.DisplayMaterial()
        self.material.Diffuse = System.Drawing.Color.OrangeRed
        self.material.IsTwoSided = True
        self.material.Shine = 0.8
    
    def CalculateBoundingBox(self, calculateBoundingBoxEventArgs):
        calculateBoundingBoxEventArgs.BoundingBox.Union(self.drawObjsBBox)
    
    def PreDrawObjects(self, drawEventArgs):
        drawEventArgs.Display.DrawBox(self.drawObjsBBox, System.Drawing.Color.White)
        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)

if (__name__ == "__main__"):
    Main()

If I look at this in the debugger I can see the objects are loading. And I can see the bounding box calculation is working. And I can see the object types are what I expect. What I can’t see is the objects being drawn in the viewports, Doh! :slight_smile:

Anyone have experience doing this - I’m not sure what I’m missing.

Thanks!
Mark
PS: My test file contains a sphere, a mesh of that sphere, and two curves.

Okay… actually I think this might be a bounding box problem. Because it does actually draw - but I was not seeing it because it was clipped.

Is it necessary to call the base class CalculateBoundingBox()? I attempted that with the code below (not sure if I have the correct syntax).

def CalculateBoundingBox(self, calculateBoundingBoxEventArgs):
    super().CalculateBoundingBox(calculateBoundingBoxEventArgs)
    calculateBoundingBoxEventArgs.BoundingBox.Union(self.drawObjsBBox)

I notice in the Mesh Drawing Example they don’t do it in the python code sample. But they do it in the C# sample.

Even if I do that I can’t zoom extents. Rhino reports “Unable to zoom - no objects are visible.”. Shouldn’t my bounding box call then incorporate my drawing into the bounding box computations?

Thanks,
Mark

Try calculateBoundingBoxEventArgs.IncludeBoundingBox instead.

Steve,

I tried it. It works :smile:

I did realize I needed to implement CalculateBoundingBoxZoomExtents().

Mark

I’m trying to figure out the best way to Transform these in-memory Mesh objects before I draw them.

I’ve seen the method:
DisplayPipeline.DrawObject(RhinoObject, Transform)

That’s okay, but I can’t specify a material as I can in a draw method such as:
DisplayPipeline.DrawMeshShaded(Mesh, DisplayMaterial)

Is there a method appropriate for transforming meshes? Then I could transform it and draw it with the above method. I can’t seem to find it but perhaps I’m missing something obvious (I hope I am).

Thanks,
Mark

You could use PushModelTransform and PopModelTransform
http://4.rhino3d.com/5/rhinocommon/html/M_Rhino_Display_DisplayPipeline_PushModelTransform.htm

Yes! Push and pop work great. Thanks, Steve.

Mark

Is it at all possible to draw using the other shading methods that are available in the viewport as when using real Rhino objects? I’m thinking of Technical, Artistic, Pen, etc. I’m guessing not but I feel compelled to ask because it would be really nice :smile:

Mark

That is not currently possible in a display “callback”.

Okay, so for now I’ve “baked” the objects into real Rhino objects at the end so they can be rendered nicely.

For anyone curious the post below contains some screen captures of our app running and full source code for loading 3dm files, drawing these in memory objects using a Display Conduit, and then baking them into real Rhino objects. The code is surprisingly simple.

Rhino 3dm File Load / Draw / Bake

Mark

This is very interesting, but what is the main benefit of doing this?
Is it to visualize the movement of the CNC cutting of an object, without having to load the robot arm into the document? Or can Rhino “draw” these objects faster than real Rhino objects?

Thanks for sharing!

Yes, initially we were posing the robot which was drawn as Rhino objects. Memory climbed like crazy (usually making Rhino crash after a while) and it was very slow. Using the display conduit keeps the memory from growing and it is MUCH faster.

Mark

Great, that is very interesting!
I look forward to looking into this.

Animating objects by replacing instances in the Rhino document is usually not the best way to go since copies of the objects are constantly being made and placed on the undo stack. Any sort of animation is typically done with something like a conduit that Mark implemented.

1 Like