From e72e32966a98bfa4ef79bb622ad0e06bd2c3cace Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Mon, 24 Feb 2020 16:41:04 +0100 Subject: [PATCH 1/5] Fixes PolylineCurve name, fixes default Rhino material name, shows type of unsupported object --- import_3dm/converters/__init__.py | 3 ++- import_3dm/converters/curve.py | 2 +- import_3dm/read3dm.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/import_3dm/converters/__init__.py b/import_3dm/converters/__init__.py index 34eac43..f70ead1 100644 --- a/import_3dm/converters/__init__.py +++ b/import_3dm/converters/__init__.py @@ -22,7 +22,8 @@ import rhino3dm as r3d -from .material import handle_materials, material_name + +from .material import handle_materials, material_name, DEFAULT_RHINO_MATERIAL from .layers import handle_layers from .render_mesh import import_render_mesh from .curve import import_curve diff --git a/import_3dm/converters/curve.py b/import_3dm/converters/curve.py index 5af5a91..d467d5c 100644 --- a/import_3dm/converters/curve.py +++ b/import_3dm/converters/curve.py @@ -65,7 +65,7 @@ def import_polyline(rcurve, bcurve, scale): return polyline -CONVERT[r3d.Polylinecurve] = import_polyline +CONVERT[r3d.PolylineCurve] = import_polyline def import_nurbs_curve(rcurve, bcurve, scale): diff --git a/import_3dm/read3dm.py b/import_3dm/read3dm.py index f48f589..4360eb5 100644 --- a/import_3dm/read3dm.py +++ b/import_3dm/read3dm.py @@ -169,7 +169,7 @@ def read_3dm(context, options): # Handle layers converters.handle_layers(context, model, toplayer, layerids, materials, update_materials, import_hidden_layers) - materials[converters.material.DEFAULT_RHINO_MATERIAL] = None + materials[converters.DEFAULT_RHINO_MATERIAL] = None #build skeletal hierarchy of instance definitions as collections (will be populated by object importer) if import_instances: @@ -181,7 +181,7 @@ def read_3dm(context, options): # Skip unsupported object types early if og.ObjectType not in converters.RHINO_TYPE_TO_IMPORT and og.ObjectType != r3d.ObjectType.InstanceReference: - print("Unsupported object type... ") + print("Unsupported object type: {}".format(og.ObjectType)) continue #convert_rhino_object = converters.RHINO_TYPE_TO_IMPORT[og.ObjectType] From f2beeb85a20d905e4e815c91b92615f855da0bf1 Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Fri, 6 Mar 2020 14:27:34 +0100 Subject: [PATCH 2/5] Adds basic support for Pointcloud objects --- import_3dm/converters/__init__.py | 2 ++ import_3dm/converters/pointcloud.py | 40 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 import_3dm/converters/pointcloud.py diff --git a/import_3dm/converters/__init__.py b/import_3dm/converters/__init__.py index f70ead1..7c381fa 100644 --- a/import_3dm/converters/__init__.py +++ b/import_3dm/converters/__init__.py @@ -30,6 +30,7 @@ from .views import handle_views from .groups import handle_groups from .instances import import_instance_reference, handle_instance_definitions, populate_instance_definitions +from .pointcloud import import_pointcloud ''' Dictionary mapping between the Rhino file types and importer functions @@ -40,6 +41,7 @@ r3d.ObjectType.Extrusion : import_render_mesh, r3d.ObjectType.Mesh : import_render_mesh, r3d.ObjectType.Curve : import_curve, + r3d.ObjectType.PointSet: import_pointcloud, #r3d.ObjectType.InstanceReference : import_instance_reference } diff --git a/import_3dm/converters/pointcloud.py b/import_3dm/converters/pointcloud.py new file mode 100644 index 0000000..22b2461 --- /dev/null +++ b/import_3dm/converters/pointcloud.py @@ -0,0 +1,40 @@ +# MIT License + +# Copyright (c) 2018-2019 Nathan Letwory, Joel Putnam, Tom Svilans, Lukas Fertig + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import rhino3dm as r3d +from . import utils + + +def import_pointcloud(context, ob, name, scale, options): + + og = ob.Geometry + oa = ob.Attributes + + vertices = [] + + # add points as mesh vertices + vertices.extend([(og[v].X * scale, og[v].Y * scale, og[v].Z * scale) for v in range(og.Count)]) + + pointcloud = context.blend_data.meshes.new(name=name) + pointcloud.from_pydata(vertices, [], []) + + return pointcloud From a12cebda94803d3965d725676142cb4cbb6a3e17 Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Fri, 6 Mar 2020 14:35:22 +0100 Subject: [PATCH 3/5] Small cleanup in pointcloud.py --- import_3dm/converters/pointcloud.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/import_3dm/converters/pointcloud.py b/import_3dm/converters/pointcloud.py index 22b2461..de52dad 100644 --- a/import_3dm/converters/pointcloud.py +++ b/import_3dm/converters/pointcloud.py @@ -29,10 +29,12 @@ def import_pointcloud(context, ob, name, scale, options): og = ob.Geometry oa = ob.Attributes - vertices = [] - # add points as mesh vertices - vertices.extend([(og[v].X * scale, og[v].Y * scale, og[v].Z * scale) for v in range(og.Count)]) + + # The following line crashes. Seems rhino3dm does not like iterating over pointclouds. + #vertices = [(p.X * scale, p.Y * scale, p.Z * scale) for p in og] + + vertices = [(og[v].X * scale, og[v].Y * scale, og[v].Z * scale) for v in range(og.Count)] pointcloud = context.blend_data.meshes.new(name=name) pointcloud.from_pydata(vertices, [], []) From 6b47a05d7fcfd7712547fa8943b22a764a064bf2 Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Fri, 20 Mar 2020 11:22:33 +0100 Subject: [PATCH 4/5] Separate dependency installation, use pip through built-in module --- import_3dm/install_dependencies.py | 69 +++++++++++++++++++ import_3dm/read3dm.py | 106 +++-------------------------- 2 files changed, 80 insertions(+), 95 deletions(-) create mode 100644 import_3dm/install_dependencies.py diff --git a/import_3dm/install_dependencies.py b/import_3dm/install_dependencies.py new file mode 100644 index 0000000..4e19f3d --- /dev/null +++ b/import_3dm/install_dependencies.py @@ -0,0 +1,69 @@ +import os, sys, bpy +import ctypes + +def modules_path(): + # set up addons/modules under the user + # script path. Here we'll install the + # dependencies + modulespath = os.path.normpath( + os.path.join( + bpy.utils.script_path_user(), + "addons", + "modules" + ) + ) + if not os.path.exists(modulespath): + os.makedirs(modulespath) + + # set user modules path at beginning of paths for earlier hit + if sys.path[1] != modulespath: + sys.path.insert(1, modulespath) + + return modulespath + + +def install_dependencies(): + import sys + import os + try: + try: + import pip + except: + print("Installing pip... "), + from subprocess import run as sprun + res = sprun([bpy.app.binary_path_python, "-m", "ensurepip"]) + + if res.returncode == 0: + import pip + else: + raise Exception("Failed to install pip.") + + modulespath = modules_path() + + if not os.path.exists(modulespath): + os.makedirs(modulespath) + + print("Installing rhino3dm to {}... ".format(modulespath)), + import subprocess + res = subprocess.run([bpy.app.binary_path_python, "-m", "pip", "install", "-q", "-t", "{}".format(modulespath), "rhino3dm"]) + + except: + raise Exception("Failed to install dependencies. Please make sure you have pip installed.") + + +if __name__ == "__main__": + try: + import rhino3dm as r3d + except: + print("Failed to load rhino3dm.") + from sys import platform + if platform == "win32": + if ctypes.windll.shell32.IsUserAnAdmin(): + install_dependencies() + import rhino3dm + else: + ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1) + + else: + print("Platform {} cannot automatically install dependencies.".format(platform)) + raise \ No newline at end of file diff --git a/import_3dm/read3dm.py b/import_3dm/read3dm.py index 4360eb5..582ce56 100644 --- a/import_3dm/read3dm.py +++ b/import_3dm/read3dm.py @@ -20,110 +20,26 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import os.path import bpy -import sys import os -import site -def modules_path(): - # set up addons/modules under the user - # script path. Here we'll install the - # dependencies - modulespath = os.path.normpath( - os.path.join( - bpy.utils.script_path_user(), - "addons", - "modules" - ) - ) - if not os.path.exists(modulespath): - os.makedirs(modulespath) +''' +Import rhino3dm and attempt install if not found +''' - # set user modules path at beginning of paths for earlier hit - if sys.path[1] != modulespath: - sys.path.insert(1, modulespath) - - return modulespath - -modules_path() - -def install_dependencies(): - modulespath = modules_path() - - try: - from subprocess import run as sprun - try: - import pip - except: - print("Installing pip... "), - pyver = "" - if sys.platform != "win32": - pyver = "python{}.{}".format( - sys.version_info.major, - sys.version_info.minor - ) - - ensurepip = os.path.normpath( - os.path.join( - os.path.dirname(bpy.app.binary_path_python), - "..", "lib", pyver, "ensurepip" - ) - ) - # install pip using the user scheme using the Python - # version bundled with Blender - res = sprun([bpy.app.binary_path_python, ensurepip, "--user"]) - - if res.returncode == 0: - import pip - else: - raise Exception("Failed to install pip.") - - print("Installing rhino3dm to {}... ".format(modulespath)), - - # if we eventually want to pin a certain version - # we can add here something like "==0.0.5". - # for now assume latest available is ok - rhino3dm_version="" - - pip3 = "pip3" - if sys.platform=="darwin": - pip3 = os.path.normpath( - os.path.join( - os.path.dirname(bpy.app.binary_path_python), - "..", - "bin", - pip3 - ) - ) - - # call pip in a subprocess so we don't have to mess - # with internals. Also, this ensures the Python used to - # install pip is going to be used - res = sprun([pip3, "install", "--upgrade", "--target", modulespath, "rhino3dm{}".format(rhino3dm_version)]) - if res.returncode!=0: - print("Please try manually installing rhino3dm with: pip3 install --upgrade --target {} rhino3dm".format(modulespath)) - raise Exception("Failed to install rhino3dm. See console for manual install instruction.") - except: - raise Exception("Failed to install dependencies. Please make sure you have pip installed.") - - -# TODO: add update mechanism try: import rhino3dm as r3d -except: - print("Failed to load rhino3dm, trying to install automatically...") - try: - install_dependencies() - # let user restart Blender, reloading of rhino3dm after automated - # install doesn't always work, better to just fail clearly before - # that - raise Exception("Please restart Blender.") - except: - raise +except ModuleNotFoundError as error: + print("Rhino3dm not found. Attempting to install dependencies...") + from .install_dependencies import install_dependencies + install_dependencies() from . import converters +''' +Import Rhinoceros .3dm file +''' + def read_3dm(context, options): filepath = options.get("filepath", "") From bfefbb6185be5fa4be219306bdd60f87009ebb39 Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Tue, 13 Oct 2020 09:03:52 +0200 Subject: [PATCH 5/5] feat(mesh): add support for vertex colors --- import_3dm/converters/render_mesh.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/import_3dm/converters/render_mesh.py b/import_3dm/converters/render_mesh.py index ea379e5..58e1478 100644 --- a/import_3dm/converters/render_mesh.py +++ b/import_3dm/converters/render_mesh.py @@ -58,6 +58,7 @@ def import_render_mesh(context, ob, name, scale, options): fidx = 0 faces = [] vertices = [] + colors = [] # now add all faces and vertices to the main lists for m in msh: if not m: @@ -73,10 +74,21 @@ def import_render_mesh(context, ob, name, scale, options): fidx = fidx + len(m.Vertices) vertices.extend([(m.Vertices[v].X * scale, m.Vertices[v].Y * scale, m.Vertices[v].Z * scale) for v in range(len(m.Vertices))]) + + if len(m.VertexColors) > 0: + colors.extend([(m.VertexColors[v][0] / 255., m.VertexColors[v][1] / 255., m.VertexColors[v][2] / 255., m.VertexColors[v][3] / 255.) for v in range(len(m.VertexColors))]) mesh = context.blend_data.meshes.new(name=name) mesh.from_pydata(vertices, [], faces) + # if it has vertex colors, add them + if len(colors) > 0: + vcol_layer = mesh.vertex_colors.new() + for poly in mesh.polygons: + for loop_index in poly.loop_indices: + loop_vert_index = mesh.loops[loop_index].vertex_index + vcol_layer.data[loop_index].color = colors[loop_vert_index] + # done, now add object to blender