Skip to content

Instantly share code, notes, and snippets.

@yorikvanhavre
Last active November 30, 2023 13:29
Show Gist options
  • Save yorikvanhavre/680156f59e2b42df8f5f5391cae2660b to your computer and use it in GitHub Desktop.
Save yorikvanhavre/680156f59e2b42df8f5f5391cae2660b to your computer and use it in GitHub Desktop.
io_import_fcstd_280.py
bl_info = {
"name": "FreeCAD Importer",
"category": "Import-Export",
"author": "Yorik van Havre",
"version": (5, 0, 0),
"blender": (2, 80, 0),
"location": "File > Import > FreeCAD",
"description": "Imports a .FCStd file from FreeCAD",
"warning": "This addon needs FreeCAD installed on your system. Only Part- and Mesh-based objects supported at the moment.",
}
# DESCRIPTION
# This script imports FreeCAD .FCStd files into Blender. This is a work in
# progress, so not all geometry elements of FreeCAD might be suported at
# this point. The development of this addon happens on the FreeCAD forum
# at https://forum.freecadweb.org (no thread yet, please
# create one ;) !)
# WARNING
# This addon requires FreeCAD to be installed on your system.
# A word of warning, your version of FreeCAD must be compiled
# with the same version of python as Blender. The first two
# numbers of the python version must be the same. For example,
# if Blender is using Pyhon 3.7.2, your version of FreeCAD must
# use Python 3.7 too (the third number after 3.7
#
# Once you have a Python3 version of FreeCAD installed, the FreeCAD
# Python module must be known to Blender. There are several ways to obtain
# this:
#
# 1) Set the correct path to FreeCAD.so (or FreeCAD.pyd on windows) in
# the Addons preferences in user settings, there is a setting for
# that under the addon panel
#
# 2) Copy or symlink FreeCAD.so (or FreeCAD.pyd on windows) to one of the
# directories from the list you get when doing this in a Python console:
#
# import sys; print(sys.path)
#
# On Debian/Ubuntu and most Linux systems, an easy way to do this is is
# to symlink FreeCAD.so to your local (user) python modules folder:
#
# ln -s /path/to/FreeCAD.so /home/YOURUSERNAME/.local/lib/python3.6/site-packages
#
# (make sure to use the same python version your blender is using instead
# of 3.6)
#
# 3) A more brutal way if the others fail is to uncomment the following line
# and set the correct path to where your FreeCAD.so or FreeCAD.pyd resides:
#
# import sys; sys.path.append("/path/to/FreeCAD.so")
#
# A simple way to test if everything is OK is to enter the following line
# in the Python console of Blender. If no error message appears,
# everything is fine:
#
# import FreeCAD
# TODO
# support clones + hires
# support texts, dimensions, etc (non-part/mesh objects)
# HISTORY
# v1.0.0 - 12 june 2018 - initial release - basically working
# v2.0.0 - 21 june 2018 - option to turn cycles mat on/off, per-face material support,
# use of polygons when possible, shared materials
# v3.0.0 - 06 february 2019 - ported to Blender 2.80
# v4.0.0 - 07 february 2019 - API changes + support of transparency
# v5.0.0 - 13 august 2019 - small fixes and better info messages if things go wrong
import sys, bpy, xml.sax, zipfile, os
from bpy_extras.node_shader_utils import PrincipledBSDFWrapper
TRIANGULATE = False # set to True to triangulate all faces (will loose multimaterial info)
class FreeCAD_xml_handler(xml.sax.ContentHandler):
"""A XML handler to process the FreeCAD GUI xml data"""
# this creates a dictionary where each key is a FC object name,
# and each value is a dictionary of property:value pairs
def __init__(self):
self.guidata = {}
self.current = None
self.properties = {}
self.currentprop = None
self.currentval = None
# Call when an element starts
def startElement(self, tag, attributes):
if tag == "ViewProvider":
self.current = attributes["name"]
elif tag == "Property":
name = attributes["name"]
if name in ["Visibility","ShapeColor","Transparency","DiffuseColor"]:
self.currentprop = name
elif tag == "Bool":
if attributes["value"] == "true":
self.currentval = True
else:
self.currentval = False
elif tag == "PropertyColor":
c = int(attributes["value"])
r = float((c>>24)&0xFF)/255.0
g = float((c>>16)&0xFF)/255.0
b = float((c>>8)&0xFF)/255.0
self.currentval = (r,g,b)
elif tag == "Integer":
self.currentval = int(attributes["value"])
elif tag == "Float":
self.currentval = float(attributes["value"])
elif tag == "ColorList":
self.currentval = attributes["file"]
# Call when an elements ends
def endElement(self, tag):
if tag == "ViewProvider":
if self.current and self.properties:
self.guidata[self.current] = self.properties
self.current = None
self.properties = {}
elif tag == "Property":
if self.currentprop and (self.currentval != None):
self.properties[self.currentprop] = self.currentval
self.currentprop = None
self.currentval = None
def import_fcstd(filename,
update=True,
placement=True,
tessellation=1.0,
skiphidden=True,
scale=1.0,
sharemats=True,
report=None):
"""Reads a FreeCAD .FCStd file and creates Blender objects"""
try:
# append the FreeCAD path specified in addon preferences
user_preferences = bpy.context.preferences
addon_prefs = user_preferences.addons[__name__].preferences
path = addon_prefs.filepath
if path:
if os.path.isfile(path):
path = os.path.dirname(path)
print("Configured FreeCAD path:",path)
sys.path.append(path)
else:
print("FreeCAD path is not configured in preferences")
import FreeCAD
except:
print("Unable to import the FreeCAD Python module. Make sure it is installed on your system")
print("and compiled with Python3 (same version as Blender).")
print("It must also be found by Python, you might need to set its path in this Addon preferences")
print("(User preferences->Addons->expand this addon).")
if report:
report({'ERROR'},"Unable to import the FreeCAD Python module. Check Addon preferences.")
return {'CANCELLED'}
# check if we have a GUI document
guidata = {}
zdoc = zipfile.ZipFile(filename)
if zdoc:
if "GuiDocument.xml" in zdoc.namelist():
gf = zdoc.open("GuiDocument.xml")
guidata = gf.read()
gf.close()
Handler = FreeCAD_xml_handler()
xml.sax.parseString(guidata, Handler)
guidata = Handler.guidata
for key,properties in guidata.items():
# open each diffusecolor files and retrieve values
# first 4 bytes are the array length, then each group of 4 bytes is abgr
if "DiffuseColor" in properties:
#print ("opening:",guidata[key]["DiffuseColor"])
df = zdoc.open(guidata[key]["DiffuseColor"])
buf = df.read()
#print (buf," length ",len(buf))
df.close()
cols = []
for i in range(1,int(len(buf)/4)):
cols.append((buf[i*4+3],buf[i*4+2],buf[i*4+1],buf[i*4]))
guidata[key]["DiffuseColor"] = cols
zdoc.close()
#print ("guidata:",guidata)
doc = FreeCAD.open(filename)
docname = doc.Name
if not doc:
print("Unable to open the given FreeCAD file")
if report:
report({'ERROR'},"Unable to open the given FreeCAD file")
return {'CANCELLED'}
#print ("Transferring",len(doc.Objects),"objects to Blender")
# import some FreeCAD modules needed below. After "import FreeCAD" these modules become available
import Part
def hascurves(shape):
for e in shape.Edges:
if not isinstance(e.Curve,(Part.Line,Part.LineSegment)):
return True
return False
matdatabase = {} # to store reusable materials
fcstd_collection = bpy.data.collections.new("FreeCAD import")
bpy.context.scene.collection.children.link(fcstd_collection)
for obj in doc.Objects:
#print("Importing",obj.Label)
if skiphidden:
if obj.Name in guidata:
if "Visibility" in guidata[obj.Name]:
if guidata[obj.Name]["Visibility"] == False:
#print(obj.Label,"is invisible. Skipping.")
continue
verts = []
edges = []
faces = []
matindex = [] # face to material relationship
plac = None
faceedges = [] # a placeholder to store edges that belong to a face
name = "Unnamed"
if obj.isDerivedFrom("Part::Feature"):
# create mesh from shape
shape = obj.Shape
if placement:
placement = obj.Placement
shape = obj.Shape.copy()
shape.Placement = placement.inverse().multiply(shape.Placement)
if shape.Faces:
if TRIANGULATE:
# triangulate and make faces
rawdata = shape.tessellate(tessellation)
for v in rawdata[0]:
verts.append([v.x,v.y,v.z])
for f in rawdata[1]:
faces.append(f)
for face in shape.Faces:
for e in face.Edges:
faceedges.append(e.hashCode())
else:
# write FreeCAD faces as polygons when possible
for face in shape.Faces:
if (len(face.Wires) > 1) or (not isinstance(face.Surface,Part.Plane)) or hascurves(face):
# face has holes or is curved, so we need to triangulate it
rawdata = face.tessellate(tessellation)
for v in rawdata[0]:
vl = [v.x,v.y,v.z]
if not vl in verts:
verts.append(vl)
for f in rawdata[1]:
nf = []
for vi in f:
nv = rawdata[0][vi]
nf.append(verts.index([nv.x,nv.y,nv.z]))
faces.append(nf)
matindex.append(len(rawdata[1]))
else:
f = []
ov = face.OuterWire.OrderedVertexes
for v in ov:
vl = [v.X,v.Y,v.Z]
if not vl in verts:
verts.append(vl)
f.append(verts.index(vl))
# FreeCAD doesn't care about verts order. Make sure our loop goes clockwise
c = face.CenterOfMass
v1 = ov[0].Point.sub(c)
v2 = ov[1].Point.sub(c)
n = face.normalAt(0,0)
if (v1.cross(v2)).getAngle(n) > 1.57:
f.reverse() # inverting verts order if the direction is couterclockwise
faces.append(f)
matindex.append(1)
for e in face.Edges:
faceedges.append(e.hashCode())
for edge in shape.Edges:
# Treat remaining edges (that are not in faces)
if not (edge.hashCode() in faceedges):
if hascurves(edge):
dv = edge.discretize(9) #TODO use tessellation value
for i in range(len(dv)-1):
dv1 = [dv[i].x,dv[i].y,dv[i].z]
dv2 = [dv[i+1].x,dv[i+1].y,dv[i+1].z]
if not dv1 in verts:
verts.append(dv1)
if not dv2 in verts:
verts.append(dv2)
edges.append([verts.index(dv1),verts.index(dv2)])
else:
e = []
for vert in edge.Vertexes:
# TODO discretize non-linear edges
v = [vert.X,vert.Y,vert.Z]
if not v in verts:
verts.append(v)
e.append(verts.index(v))
edges.append(e)
elif obj.isDerivedFrom("Mesh::Feature"):
# convert freecad mesh to blender mesh
mesh = obj.Mesh
if placement:
placement = obj.Placement
mesh = obj.Mesh.copy() # in meshes, this zeroes the placement
t = mesh.Topology
verts = [[v.x,v.y,v.z] for v in t[0]]
faces = t[1]
if verts and (faces or edges):
# create or update object with mesh and material data
bobj = None
bmat = None
if update:
# locate existing object (mesh with same name)
for o in bpy.data.objects:
if o.data.name == obj.Name:
bobj = o
print("Replacing existing object:",obj.Label)
bmesh = bpy.data.meshes.new(name=obj.Name)
bmesh.from_pydata(verts, edges, faces)
bmesh.update()
if bobj:
# update only the mesh of existing object. Don't touch materials
bobj.data = bmesh
else:
# create new object
bobj = bpy.data.objects.new(obj.Label, bmesh)
if placement:
#print ("placement:",placement)
bobj.location = placement.Base.multiply(scale)
m = bobj.rotation_mode
bobj.rotation_mode = 'QUATERNION'
if placement.Rotation.Angle:
# FreeCAD Quaternion is XYZW while Blender is WXYZ
q = (placement.Rotation.Q[3],)+placement.Rotation.Q[:3]
bobj.rotation_quaternion = (q)
bobj.rotation_mode = m
bobj.scale = (scale,scale,scale)
if obj.Name in guidata:
if matindex and ("DiffuseColor" in guidata[obj.Name]) and (len(matindex) == len(guidata[obj.Name]["DiffuseColor"])):
# we have per-face materials. Create new mats and attribute faces to them
fi = 0
objmats = []
for i in range(len(matindex)):
# DiffuseColor stores int values, Blender use floats
rgba = tuple([float(x)/255.0 for x in guidata[obj.Name]["DiffuseColor"][i]])
# FreeCAD stores transparency, not alpha
alpha = 1.0
if rgba[3] > 0:
alpha = 1.0-rgba[3]
rgba = rgba[:3]+(alpha,)
bmat = None
if sharemats:
if rgba in matdatabase:
bmat = matdatabase[rgba]
if not rgba in objmats:
objmats.append(rgba)
bobj.data.materials.append(bmat)
if not bmat:
if rgba in objmats:
bmat = bobj.data.materials[objmats.index(rgba)]
if not bmat:
bmat = bpy.data.materials.new(name=obj.Name+str(len(objmats)))
bmat.use_nodes = True
principled = PrincipledBSDFWrapper(bmat, is_readonly=False)
principled.base_color = rgba[:3]
if alpha < 1.0:
bmat.diffuse_color = rgba
principled.alpha = alpha
bmat.blend_method = "BLEND"
objmats.append(rgba)
bobj.data.materials.append(bmat)
if sharemats:
matdatabase[rgba] = bmat
for fj in range(matindex[i]):
bobj.data.polygons[fi+fj].material_index = objmats.index(rgba)
fi += matindex[i]
else:
# one material for the whole object
alpha = 1.0
rgb = (0.5,0.5,0.5)
if "Transparency" in guidata[obj.Name]:
if guidata[obj.Name]["Transparency"] > 0:
alpha = (100-guidata[obj.Name]["Transparency"])/100.0
if "ShapeColor" in guidata[obj.Name]:
rgb = guidata[obj.Name]["ShapeColor"]
rgba = rgb+(alpha,)
bmat = None
if sharemats:
if rgba in matdatabase:
bmat = matdatabase[rgba]
else:
#print("not found in db:",rgba,"in",matdatabase)
pass
if not bmat:
bmat = bpy.data.materials.new(name=obj.Name)
# no more internal engine!
# bmat.diffuse_color = rgb
# bmat.alpha = alpha
#if enablenodes:
bmat.use_nodes = True
principled = PrincipledBSDFWrapper(bmat, is_readonly=False)
principled.base_color = rgb
if alpha < 1.0:
bmat.diffuse_color = rgba
if sharemats:
matdatabase[rgba] = bmat
bobj.data.materials.append(bmat)
fcstd_collection.objects.link(bobj)
#bpy.context.scene.objects.active = obj
#obj.select = True
FreeCAD.closeDocument(docname)
# why is this here? I don't remember. It doesn't seem to work anymore anyway...
#for area in bpy.context.screen.areas:
# if area.type == 'VIEW_3D':
# for region in area.regions:
# if region.type == 'WINDOW':
# override = {'area': area, 'region': region, 'edit_object': bpy.context.edit_object}
# bpy.ops.view3d.view_all(override)
print("Import finished without errors")
return {'FINISHED'}
#==============================================================================
# Blender Operator class
#==============================================================================
class IMPORT_OT_FreeCAD(bpy.types.Operator):
"""Imports the contents of a FreeCAD .FCStd file"""
bl_idname = 'import_fcstd.import_freecad'
bl_label = 'Import FreeCAD FCStd file'
bl_options = {'REGISTER', 'UNDO'}
# ImportHelper mixin class uses this
filename_ext = ".fcstd"
# Properties assigned by the file selection window.
directory : bpy.props.StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
files : bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'})
option_skiphidden : bpy.props.BoolProperty(name="Skip hidden objects", default=True,
description="Only import objects that where visible in FreeCAD"
)
option_update : bpy.props.BoolProperty(name="Update existing objects", default=True,
description="Keep objects with same names in current scene and their materials, only replace the geometry"
)
option_placement : bpy.props.BoolProperty(name="Use Placements", default=True,
description="Set Blender pivot points to the FreeCAD placements"
)
option_tessellation : bpy.props.FloatProperty(name="Tessellation value", default=1.0,
description="The tessellation value to apply when triangulating shapes"
)
option_scale : bpy.props.FloatProperty(name="Scaling value", default=0.001,
description="A scaling value to apply to imported objects. Default value of 0.001 means one Blender unit = 1 meter"
)
option_sharemats : bpy.props.BoolProperty(name="Share similar materials", default=True,
description="Objects with same color/transparency will use the same material"
)
# invoke is called when the user picks our Import menu entry.
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
# execute is called when the user is done using the modal file-select window.
def execute(self, context):
dir = self.directory
for file in self.files:
filestr = str(file.name)
if filestr.lower().endswith(".fcstd"):
return import_fcstd(filename=dir+filestr,
update=self.option_update,
placement=self.option_placement,
tessellation=self.option_tessellation,
skiphidden=self.option_skiphidden,
scale=self.option_scale,
sharemats=self.option_sharemats,
report=self.report)
return {'FINISHED'}
class IMPORT_OT_FreeCAD_Preferences(bpy.types.AddonPreferences):
"""A preferences settings dialog to set the path to the FreeCAD module"""
bl_idname = __name__
filepath : bpy.props.StringProperty(
name="Path to FreeCAD.so (Mac/Linux) or FreeCAD.pyd (Windows)",
subtype='FILE_PATH',
)
def draw(self, context):
layout = self.layout
layout.label(text="FreeCAD must be installed on your system, and its path set below. Make sure both FreeCAD and Blender use the same Python version (check their Python console)")
layout.prop(self, "filepath")
#==============================================================================
# Register plugin with Blender
#==============================================================================
classes = (
IMPORT_OT_FreeCAD,
IMPORT_OT_FreeCAD_Preferences,
)
# needed if you want to add into a dynamic menu
def menu_func_import(self, context):
self.layout.operator(IMPORT_OT_FreeCAD.bl_idname, text="FreeCAD (.FCStd)")
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
if __name__ == "__main__":
register()
Copy link

ghost commented Aug 26, 2019

Hi.
Line 167 and 169 are missing a closing parenthesis ')'

@yorikvanhavre
Copy link
Author

That's what you get of editing at the last minute without testing 😄
Thanks for notifying! It's fixed now

@giacomomarchioro
Copy link

Wonderful idea! But it's a pity that there is such an incompatibility between Python 3.6 and 3.7 . I wonder if is not possible to load directly the file without using the FreeCAD libraries, this would make the import easier. At the end, the xml files inside the .fcstd should contain all the information. Am I wrong?

@alxscott
Copy link

Apologies for commenting here but I wasn't sure if FreeCAD forum or Blender was best, but decided to go straight to the author.

Im am running MacOS 10.15, with Blender 2.8 and FreeCAD 0.19 ( For Python comparability). I have installed the Add-on via the Blender "Preferences -> Add-on -> Install from File". I have added the FreeCAD.so path and restarted Blender. When I import a .fcstd file; the screenshot below appears. I have tried running "import FreeCAD" in the Blender python console, no error is shown.

Screenshot 2019-10-20 at 15 07 24

Thank you in advance.

@SchmidiPS
Copy link

Apologies for commenting here but I wasn't sure if FreeCAD forum or Blender was best, but decided to go straight to the author.

Im am running MacOS 10.15, with Blender 2.8 and FreeCAD 0.19 ( For Python comparability). I have installed the Add-on via the Blender "Preferences -> Add-on -> Install from File". I have added the FreeCAD.so path and restarted Blender. When I import a .fcstd file; the screenshot below appears. I have tried running "import FreeCAD" in the Blender python console, no error is shown.

Screenshot 2019-10-20 at 15 07 24

Thank you in advance.

Hi,

I've the same problem...
Maybe I am doing something wrong, but I do not figure out how to solve this issue.
Thank you for your work!!

Kind regards!

@yorikvanhavre
Copy link
Author

Wonderful idea! But it's a pity that there is such an incompatibility between Python 3.6 and 3.7 . I wonder if is not possible to load directly the file without using the FreeCAD libraries, this would make the import easier. At the end, the xml files inside the .fcstd should contain all the information. Am I wrong?

It's not that simple unfortunately... All the geometry information is stored in the Brep files. Reconstructing everything from the xml file is not always possible, and in any case it would be almost like reprogramming a new FreeCAD engine from scratch as it would need to recalculate all the geometry without using its current OCC engine

@yorikvanhavre
Copy link
Author

Apologies for commenting here but I wasn't sure if FreeCAD forum or Blender was best, but decided to go straight to the author.
Im am running MacOS 10.15, with Blender 2.8 and FreeCAD 0.19 ( For Python comparability). I have installed the Add-on via the Blender "Preferences -> Add-on -> Install from File". I have added the FreeCAD.so path and restarted Blender. When I import a .fcstd file; the screenshot below appears. I have tried running "import FreeCAD" in the Blender python console, no error is shown.
Screenshot 2019-10-20 at 15 07 24
Thank you in advance.

Sorry guys, forgot to look here. This is really weird... But I have no mac to test unfortunately... You'll have to help me I guess...
Can you try this in the Python console?

import FreeCAD
print(dir(FreeCAD))
print(FreeCAD.__file__)

@zohozer
Copy link

zohozer commented Apr 21, 2020

Hi. I am trying to make this add-on to work but I don't understand how to install-it properly.

I am using MacOS 10.14 with the latest FreeCAD v0.19 version from Github and Blender 2.82 stable release.

When I try to activate the add-on, I have this error message:

Blender2 82_FreeCAD_importer

How can I define the path to the FreeCAD app, so that the addon can find my FreeCAD install?

P.S. Seems that FreeCAD it is using Python 3.8.2 and Blender 3.7.2. How can I make Blender to load the 3.8.2 version of Python?

@yorikvanhavre
Copy link
Author

yorikvanhavre commented Apr 21, 2020

How can I define the path to the FreeCAD app, so that the addon can find my FreeCAD install?

Inside Blender:

import sys
print(sys.path)

This will show you all the folders where Python looks for modules. the FreeCAD.so file must be in one of them.
The easiest way is to locate your FreeCAD.so file, and then set its path in the addon preferences in Blender. Another solution is to create a symbolic link (not sure how to do that on Mac...) from your FreeCAD.so file into one of the folders given by the command above.

P.S. Seems that FreeCAD it is using Python 3.8.2 and Blender 3.7.2. How can I make Blender to load the 3.8.2 version of Python?

You cannot do that on the fly. The python version is built-in, hard-coded when FreeCAD and Blender are compiled (this is because they are both C++ applications that embed the C++ code of Python). So your only solution is to replace either your version of Blender or FreeCAD with one that has matching Python version...

@zohozer
Copy link

zohozer commented Apr 21, 2020

Hi Yorik. Thank you for your answer.

On MacOS, I do found the FreeCAD.so located here: "/Applications/FreeCAD.app/Contents/Resources/lib/FreeCAD.so"

After creating the symplink using the command: ln -s /Applications/FreeCAD.app/Contents/Resources/lib/FreeCAD.so /Users/user/.local/lib/python3.7/site-packages , I do get another error but I do think that this is because the different Python version used by FreeCAD.

Blender_FreeCAD_Addon_activation_error

It is on internet an archive with different FreeCAD builds? I can't find a FreeCAD MacOS build with Python 3.7.

@zohozer
Copy link

zohozer commented Apr 25, 2020

I found this package: "osx-64/freecad-0.19.pre-py37hdbba661_85.tar.bz2" from: https://anaconda.org/freecad/freecad/files?page=2

but after unarchiving I get this error when activating the "FreeCAD Importer Addon" on Blender:

Traceback (most recent call last):
  File "/Applications/Blender/Blender_2.82.app/Contents/Resources/2.82/scripts/modules/addon_utils.py", line 351, in enable
    mod = __import__(module_name)
ImportError: dlopen(/Users/winner/.local/lib/python3.7/site-packages/FreeCAD.so, 2): no suitable image found.  Did find:
	/Users/user/.local/lib/python3.7/site-packages/FreeCAD.so: unknown file type, first eight bytes: 0x7F 0x45 0x4C 0x46 0x02 0x01 0x01 0x00
	/Users/user/.local/lib/python3.7/site-packages/FreeCAD.so: stat() failed with errno=60

@yorikvanhavre
Copy link
Author

I found this package: "osx-64/freecad-0.19.pre-py37hdbba661_85.tar.bz2" from: https://anaconda.org/freecad/freecad/files?page=2
but after unarchiving I get this error when activating the "FreeCAD Importer Addon" on Blender:

It is hard for me to say what's wrong because I'm not familiar with MacOS, but by the look of it, either FreeCAD was not correctly installed (FreeCAD.so file is corrupt somehow), or there is a python version mismatch. Make sure your Blender Python version is 3.7.X (the third number doesn't matter)? You find that info in the Python console of Blender

@QuakeString
Copy link

Hi. I am trying to make this add-on to work but I don't understand how to install-it properly.

I am using MacOS 10.14 with the latest FreeCAD v0.19 version from Github and Blender 2.82 stable release.

When I try to activate the add-on, I have this error message:

Blender2 82_FreeCAD_importer

How can I define the path to the FreeCAD app, so that the addon can find my FreeCAD install?

P.S. Seems that FreeCAD it is using Python 3.8.2 and Blender 3.7.2. How can I make Blender to load the 3.8.2 version of Python?

I got the same issue, but I also did not understood author's reply how to perform it.

@yorikvanhavre
Copy link
Author

Like said above, there is no way to do that. Python is compiled inside the application (in both FreeCAD and Blender), you cannot change it on the fly. The only way to change the python version is to recompile one of the two with another version of Python. If FreeCAD is using Python 3.8.2 and Blender 3.7.2, you must either find a version of FreeCAD that uses Python 3.7.XXX or a version of Blender that uses Python 3.8.XXX (the third number doesn't matter). I believe the latter option will be easier, as Python 3.8 is more or less the default nowadays

@trianglesis
Copy link

trianglesis commented Nov 15, 2020

What is strange in my case - on windows - I have no FreeCAD.pyd on 0.18 (installer) and 0.19 (Git repo zip) versions of FreeCAD.
Alphabetically I have FemGUI.pyd and then - Image.pyd

  • Found in: D:\FreeCAD\FreeCAD_0.19\bin
    But now:
  • Unable to import the FreeCAD Python module. Check Addon preferences.

Blender: (PYTHON INTERACTIVE CONSOLE 3.7.7 (default, Jun 13 2020, 11:11:23))
FreeCAD: (Python 3.6.8 (v3.6.8:3c6b436a57, Mar 26 2019, 15:45:44))

@yorikvanhavre
Copy link
Author

yorikvanhavre commented Nov 16, 2020

It should be in D:\FreeCAD\FreeCAD_0.19\lib I think
Please read the comments above... You need the same version of Python in both Blender and FreeCAD

@melonius
Copy link

melonius commented Nov 18, 2020

Blender 2.90.1
FreeCAD 0.19 (22756) AppImage unpacked
Debian 10

When I try to activate the addon, I get:

Traceback (most recent call last):
  File "/home/mel/Applications/blender-2.90.1-linux64/2.90/scripts/modules/addon_utils.py", line 351, in enable
    mod = __import__(module_name)
ModuleNotFoundError: No module named 'FreeCAD '

Does not appear the "Preferences" that appears in the video:
image

How can I tell blender where is the FreeCad.so?

@yorikvanhavre
Copy link
Author

Please check the Python version of both FreeCAD and Blender, as explained above

@vehrmann
Copy link

Hi Yorik,
I checked the old Blender and FreeCAD versions and couldn't find a matching pair regarding the Python versions.

All versions of Blender 2.79 to 2.91 use either Python 3.5 or Python 3.7
FreeCAD 0.17 uses Python 2.7
FreeCAD 0.18 uses Python 3.6
FreeCAD 0.19 uses Python 3.8

Could you tell me which versions you used for your script? Thanks a lot, looking forward to using your script!
Best, Vincent

@yorikvanhavre
Copy link
Author

Hi Vincent,
I compile both FreeCAD and Blender myself, so I use always the same Python version. I would suggest you search for custom builds in https://github.com/FreeCAD/FreeCAD/releases and https://builder.blender.org/download/branches/ , certainly you can find something that matches... Unfortunately on these two sites the Python version is not indicated, so I'm afraid you'll be in for some download-and-try. On the forums of those both apps there might be more custom builds too, or at least you can ask for it.

@vehrmann
Copy link

Thanks for your quick answer.
I used a conda environment like described here to get FreeCAD running on Python 3.7 and it works!
Don't forget to activate the environment and set the correct path within the Blender Addon section. Mine looks like this: C:\ProgramData\Anaconda3\envs\freecad37\Library\bin\FreeCAD.pyd

@QuakeString
Copy link

QuakeString commented May 10, 2021

I'm facing this error. even though my FreeCAD 0.19.2 python version 3.9.3 and blender 2.92 python version 3.9.4
I understand previously I had same issue but that time my FC an blender has same python version 3.9.xx
Screenshot_20210511_001249
Screenshot_20210511_001609
Screenshot_20210511_001520

@vehrmann
Copy link

vehrmann commented Dec 1, 2021

@QuakeString
A late answer, but I hope it helps you or someone else.

I think you might have the same issue I was figuring out for the last hour (although I apparently had no issue installing the addon a couple of months ago...): your file name of the .py-file might be wrong!
First I saved it as "FreeCAD .FCStd importer for Blender 2.80.py" and I got the same error message like you.
When naming it "io_import_fcstd_280.py" the error didn't occur and I am able to type in a FreeCAD path in Blender's addon manager and importing a FreeCAD file successfully.
I am using Blender 2.93.6 (built with Python 3.9.2) with a conda version of FreeCAD 0.19 using Python 3.9.6.

Also helpful might be the following video: https://www.youtube.com/watch?v=sCs8xlrw2nM

Thanks again to @yorikvanhavre for this great plugin! Maybe you could put the hint with the file name into the manual?

@s-light
Copy link

s-light commented Jul 13, 2023

if you like to have a full blender plugin - based on this code here -
have a look at
https://github.com/s-light/io_import_fcstd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment