Python Export obj files - set polygon density variable

Hello. I have created the following script that works well! The mcneely github py files are very informative. This is also my first python project ever too, as well as my first Rhino project ever. I am using Atom on Mac with Rhino Version 5.0 WIP 5A761w.

I have added checks for nested layers, empty layers, invisible layers, etc. What I would like to do now is before calling the final enter, is to set the Polygon Density of the final mesh process. I don’t understand how this works. I set the density very low and yet my obj files are the same size.

rs.Command("_-Export "+filePath+name+extension+" _Enter PolygonDensity=1 _Enter")

How does this option work and is there an alternative method for reducing and perhaps also smoothing the mesh?

import os
import scriptcontext
import rhinoscriptsyntax as rs


print "//export run started/////////////"

# this function via mcneel/rhinoscriptsyntax
#https://github.com/mcneel/rhinoscriptsyntax/blob/master/Scripts/rhinoscript/layer.py
def layerNames(sort=False):
    rc = []
    for layer in scriptcontext.doc.Layers:
        if not layer.IsDeleted: rc.append(layer.FullPath)
    if sort: rc.sort()
    return rc


fileName = rs.DocumentName()
filePath = rs.DocumentPath().rstrip(fileName)
extension = ".obj"

arrLayers = layerNames(False)


def initExport():
    for layerName in arrLayers:
        layer = scriptcontext.doc.Layers.FindByFullPath(layerName, True)
        if layer >= 0:
            layer = scriptcontext.doc.Layers[layer]
            if layer.IsVisible and rs.IsLayerEmpty(layerName) == False:
                cutName = layerName.split("::")
                cutName = cutName[len(cutName)-1]
                objs = scriptcontext.doc.Objects.FindByLayer(cutName)
                if len(objs) > 0:
                    saveObjectsToFile(cutName, objs)

def saveObjectsToFile(name, objs):
    if len(objs) > 0:
        rs.UnselectAllObjects()
        for obj in objs:
            obj.Select(True)
        scriptcontext.doc.Views.Redraw()
        name = "".join(name.split(" "))
        rs.Command("_-Export "+filePath+name+extension+" _Enter PolygonDensity=1 _Enter")

initExport()

print "//export run ended/////////////"
1 Like

Hi radio412,

instead of passing this to your rs.Command string:

... _Enter _PolygonDensity=1

you can use this:

... _Enter _DetailedOptions _AdvancedOptions

followed by these possible options and values:

_Angle=0  
_AspectRatio=0  
_Distance=0  
_Density=0.5  
_Grid=0  
_MaxEdgeLength=0  
_MinEdgeLength=0.0001

The best way to figure them out is to start _-Export command manually, click on “Browse” in the commandline, enter a filename and look what is available in the commandline. It is pretty much everything which is available in the meshing dialog. eg. Once _DetailedOptions is passed to the commandline, you have the possibility to set up these settings too:

_JaggedSeams=No
_PackTextures=Yes
_Refine=No
_SimplePlane=No 

best thing is you make the ones required as variables in your saveObjectsToFile function. Not all need to be passed.

c.

1 Like

Hi Clement

Thank you so much for the reply. I think I am starting to understand.

My first question is, I see you’re putting an underscore, and it was recommended I put an underscore before my export and Enter commands, but it doesn’t seem required for PolygonDensity. I validate this by seeing the value set in python reflected in the dialog in the interface:

rs.Command("_-Export "+filePath+name+extension+" _Enter PolygonDensity=1 ")

Is the underscore a best practice?

My seconds question is, I tried to follow your instructions to investigate the possible commands. It may be because I am on a Mac but I could not complete the steps. I type _-Export in the command line. I am presented with a couple options and a browse button. If I click the browse button, the command line goes away and Rhino presents a save dialog window. I can then save the obj file by hand in the usual way. I can see all the options obj export has available here, but they are not in the same wording. For example it took trial and error to know that “Polygon Density” correlated to “PolygonDensity” as a command.

Also,
I like the idea of adding the setting as input parameters to the saveObjectsToFile function. I was hoping to use point cloud counts to determine thresholds when a mesh ought to have PolygonDensity manipulated at all. Forgive my newness to 3d… do points make up polygons and thus lowering the polygon count reduces the number of points and thus the file size?

The _ (underscore) is just to make shure that the commands can be used in different languages of Rhino, if you would not use it, your script will work in english language version only. The - (hyphen) sign before -Export is to prevent the dialog to pop up, so you can enter everything via the commandline.

About your other quesitons: If you only have _PolygonDensity in your commandline and _DetailedOptions is missing, then i guess this is not yet hooked up in the mac version. I can not verify this from my windows PC but i hope @Helvetosaur or @pascal can chime in to clarify.

Yes, if you have a NURBS surface or polysurface and want to export it as OBJ file, it has to be approximated which means it gets meshed into polygons. These polygons are just faces (quads or triangles) using points. The density sets how far the resulting mesh is allowed to deviate from the originating NURBS object. The smaller the density, the smaller the file size will be. If you select eg. a NURBS sphere and run the _Mesh command in Rhino, you can see various options available in the dialog to control the final density of the mesh. The same options are available in the commandline when you export as OBJ file. At least this is how it works on windows.

c.

1 Like

Don’t have a batch export for .obj, just tested with .stl with the attached script and things seem to be working correctly on Mac. Will modify the .stl script to do .obj and check…

BatchExportSTLByObject.py (1.7 KB)

Edit: OK, OBJ export via python script seems to be running on Mac as well. Since I don’t know what correct settings would be for obj export, check the attached and modify the GetOBJSettings definition to suit your needs (right now they are at the defaults that Rhino presents).

BatchExportOBJByObject.py (2.0 KB)

–Mitch

2 Likes

thank you Mitch for the example scripts.

c.

1 Like

My wording was misleading. I didn’t mean to say the mac wasn’t producing the obj files as designed. I simply didn’t understand how to manipulate the output or if I understood what was happening when calling the commands. Both the explanations and sample code were perfect.

Thank you both for taking the time!

Hi again! Thank you so much for the help thus far!

The top .obj mesh sphere in the image was created with Atom/Python/Mac Rhino. The script I used is below.

I used the same settings by hand in the Rhino GUI to create the .obj mesh in the image on the bottom. They are very different though.

I am wondering if I am missing something in the Python script. Small file size with very approximate meshes are my goal from the python script. Thoughts?

import os
import scriptcontext
import rhinoscriptsyntax as rs


print "//export run started/////////////"

# this function via mcneel/rhinoscriptsyntax
#https://github.com/mcneel/rhinoscriptsyntax/blob/master/Scripts/rhinoscript/layer.py
def layerNames(sort=False):
    rc = []
    for layer in scriptcontext.doc.Layers:
        if not layer.IsDeleted: rc.append(layer.FullPath)
    if sort: rc.sort()
    return rc

def GetDAESettings():
    e_str = ""
    return e_str

def GetOBJSettings():
    e_str = "_Geometry=_Mesh "
    e_str+= "_EndOfLine=CRLF "
    e_str+= "_ExportRhinoObjectNames=_ExportObjectsAsOBJGroups "
    e_str+= "_ExportMeshTextureCoordinates=_Yes "
    e_str+= "_ExportMeshVertexNormals=_No "
    e_str+= "_CreateNGons=_No "
    e_str+= "_ExportMaterialDefinitions=_No "
    e_str+= "_YUp=_Yes "
    e_str+= "_WrapLongLines=Yes "
    e_str+= "_VertexWelding=_Welded "
    e_str+= "_WritePrecision=3 "
    e_str+= "_Enter _DetailedOptions "
    e_str+= "_JaggedSeams=_No "
    e_str+= "_PackTextures=_No "
    e_str+= "_Refine=_No "
    e_str+= "_SimplePlane=_Yes "
    e_str+= "_AdvancedOptions "
    e_str+= "_Angle=50 "
    e_str+= "_AspectRatio=0 "
    e_str+= "_Distance=0.01 "
    e_str+= "_Density=0.01 "
    e_str+= "_Grid=4 "
    e_str+= "_MaxEdgeLength=0 "
    e_str+= "_MinEdgeLength=0 "
    e_str+= "_Enter _Enter"
    return e_str

def GetSTLSettings():
    eStr = "_ExportFileAs=_Binary "
    eStr+= "_ExportUnfinishedObjects=_Yes "
    eStr+= "_UseSimpleDialog=_No "
    eStr+= "_UseSimpleParameters=_No "
    eStr+= "_Enter _DetailedOptions "
    eStr+= "_JaggedSeams=_No "
    eStr+= "_PackTextures=_No "
    eStr+= "_Refine=_Yes "
    eStr+= "_SimplePlane=_No "
    eStr+= "_AdvancedOptions "
    eStr+= "_Angle=15 "
    eStr+= "_AspectRatio=0 "
    eStr+= "_Distance=0.01 "
    eStr+= "_Grid=16 "
    eStr+= "_MaxEdgeLength=0 "
    eStr+= "_MinEdgeLength=0.0001 "
    eStr+= "_Enter _Enter"
    return eStr

settingsList = {
    'GetDAESettings': GetDAESettings,
    'GetOBJSettings': GetOBJSettings,
    'GetSTLSettings': GetSTLSettings
}




fileName = rs.DocumentName()
filePath = rs.DocumentPath().rstrip(fileName)

arrLayers = layerNames(False)



def initExportByLayer(fileType="obj", visibleonly=False, byObject=False):
    for layerName in arrLayers:
        layer = scriptcontext.doc.Layers.FindByFullPath(layerName, True)
        if layer >= 0:
            layer = scriptcontext.doc.Layers[layer]
            save = True;
            if visibleonly:
                if not layer.IsVisible:
                    save = False
            if  rs.IsLayerEmpty(layerName):
                save = False
            if save:
                cutName = layerName.split("::")
                cutName = cutName[len(cutName)-1]
                objs = scriptcontext.doc.Objects.FindByLayer(cutName)
                if len(objs) > 0:
                    if byObject:
                        i=0
                        for obj in objs:
                            i= i+1
                            saveObjectsToFile(cutName+"_"+str(i), [obj], fileType)
                    else:
                        saveObjectsToFile(cutName, objs, fileType)



def saveObjectsToFile(name, objs, fileType):
    rs.EnableRedraw(False)
    if len(objs) > 0:
        settings = settingsList["Get"+fileType.upper()+"Settings"]()
        rs.UnselectAllObjects()
        for obj in objs:
            obj.Select(True)
        name = "".join(name.split(" "))
        command = '-_Export "{}{}{}" {}'.format(filePath, name, "."+fileType.lower(), settings)
        rs.Command(command, True)
        rs.EnableRedraw(True)


initExportByLayer("obj",True, False)


print "//export run ended/////////////"

I cannot repeat the difference here (under windows), someone with a mac might try to run your script to verify.

One other thing: You might enable to export the mesh vertex normals in the OBJ export, otherwise the mesh will look pretty strange once re-imported into rhino. (tried this with a sphere).

c.

Apologies. I discovered a solution via trial and error and alerted it as a defect. I did this with a new thread since this one focused so much simply on the command structure. The thread is at Python/Atom/Mac .obj file export Density, possible defect