From 7029c6f53eb994264ddec5678b7056f6da80d606 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Tue, 10 Oct 2023 11:14:37 +0200 Subject: [PATCH] Arch: Fix multiple issues with ImportOBJ.py Fixes #6187. Other fixes: * Made handling of shapes and global placement more consistent. * Shapes without curves were also triangulated. * Polygonal faces could have flipped normals and duplicate points. --- src/Mod/Arch/importOBJ.py | 280 ++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 136 deletions(-) diff --git a/src/Mod/Arch/importOBJ.py b/src/Mod/Arch/importOBJ.py index eaa03fe4f091..f2e4022222a5 100644 --- a/src/Mod/Arch/importOBJ.py +++ b/src/Mod/Arch/importOBJ.py @@ -69,84 +69,68 @@ def getIndices(obj,shape,offsetv,offsetvn): vnlist = [] elist = [] flist = [] - curves = None + hascurve = False mesh = None if isinstance(shape,Part.Shape): for e in shape.Edges: try: - if not isinstance(e.Curve,Part.LineSegment): - if not curves: - if obj.isDerivedFrom("App::Link"): - myshape = obj.LinkedObject.Shape.copy(False) - myshape.Placement=obj.LinkPlacement - else: - myshape = obj.Shape.copy(False) - myshape.Placement = obj.getGlobalPlacement() - mesh = MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True) - FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") - break + if not isinstance(e.Curve,Part.Line): + hascurve = True except Exception: # unimplemented curve type - if obj.isDerivedFrom("App::Link"): - if obj.Shape: - myshape = obj.Shape.copy(False) - myshape.Placement=obj.LinkPlacement - else: - myshape = obj.Shape.copy(False) - myshape.Placement=obj.getGlobalPlacement() - mesh = MeshPart.meshFromShape(Shape=myshape, LinearDeflection=0.1, AngularDeflection=0.7, Relative=True) - FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") - break + hascurve = True + if hascurve: + param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Mesh") + tol = param.GetFloat("MaxDeviationExport",0.1) + mesh = Mesh.Mesh() + mesh.addFacets(shape.getFaces(tol)) + FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") + break elif isinstance(shape,Mesh.Mesh): mesh = shape - curves = shape.Topology + if mesh: for v in mesh.Topology[0]: vlist.append(" "+str(round(v[0],p))+" "+str(round(v[1],p))+" "+str(round(v[2],p))) for vn in mesh.Facets: - vnlist.append(" "+str(vn.Normal[0]) + " " + str(vn.Normal[1]) + " " + str(vn.Normal[2])) + vnlist.append(" "+str(round(vn.Normal[0],p))+" "+str(round(vn.Normal[1],p))+" "+str(round(vn.Normal[2],p))) for i, vn in enumerate(mesh.Topology[1]): flist.append(" "+str(vn[0]+offsetv)+"//"+str(i+offsetvn)+" "+str(vn[1]+offsetv)+"//"+str(i+offsetvn)+" "+str(vn[2]+offsetv)+"//"+str(i+offsetvn)+" ") else: - if curves: - for v in curves[0]: - vlist.append(" "+str(round(v.x,p))+" "+str(round(v.y,p))+" "+str(round(v.z,p))) - for f in curves[1]: - fi = "" - for vi in f: - fi += " " + str(vi + offsetv) - flist.append(fi) - else: - for v in shape.Vertexes: - vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) - if not shape.Faces: - for e in shape.Edges: - if DraftGeomUtils.geomType(e) == "Line": - ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offsetv) - ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offsetv) - elist.append(ei) - for f in shape.Faces: - if len(f.Wires) > 1: - # if we have holes, we triangulate - tris = f.tessellate(1) - for fdata in tris[1]: - fi = "" - for vi in fdata: - vdata = Part.Vertex(tris[0][vi]) - fi += " " + str(findVert(vdata,shape.Vertexes) + offsetv) - flist.append(fi) - else: + for v in shape.Vertexes: + vlist.append(" "+str(round(v.X,p))+" "+str(round(v.Y,p))+" "+str(round(v.Z,p))) + if not shape.Faces: + for e in shape.Edges: + if DraftGeomUtils.geomType(e) == "Line": + ei = " " + str(findVert(e.Vertexes[0],shape.Vertexes) + offsetv) + ei += " " + str(findVert(e.Vertexes[-1],shape.Vertexes) + offsetv) + elist.append(ei) + for f in shape.Faces: + if len(f.Wires) > 1: + # if we have holes, we triangulate + tris = f.tessellate(1) + for fdata in tris[1]: fi = "" - for e in f.OuterWire.OrderedEdges: - #print(e.Vertexes[0].Point,e.Vertexes[1].Point) - v = e.Vertexes[0] - ind = findVert(v,shape.Vertexes) - if ind is None: - return None,None,None - fi += " " + str(ind + offsetv) + for vi in fdata: + vdata = Part.Vertex(tris[0][vi]) + fi += " " + str(findVert(vdata,shape.Vertexes) + offsetv) flist.append(fi) + else: + fi = "" + edges = f.OuterWire.OrderedEdges + # Avoid flipped normals: + if f.Orientation == "Reversed": + edges.reverse() + for e in edges: + v = e.Vertexes[0 if e.Orientation == "Forward" else 1] + ind = findVert(v,shape.Vertexes) + if ind is None: + return None,None,None,None + fi += " " + str(ind + offsetv) + flist.append(fi) + return vlist,vnlist,elist,flist @@ -171,84 +155,108 @@ def export(exportList,filename,colors=None): filenamemtl = filename[:-4] + ".mtl" materials = [] outfile.write("mtllib " + os.path.basename(filenamemtl) + "\n") + for obj in objectslist: - if obj.isDerivedFrom("Part::Feature") or obj.isDerivedFrom("Mesh::Feature") or obj.isDerivedFrom("App::Link"): - hires = None - if FreeCAD.GuiUp: - visible = obj.ViewObject.isVisible() - if obj.ViewObject.DisplayMode == "HiRes": - # check if high-resolution object is available - if hasattr(obj,"HiRes"): - if obj.HiRes: - if obj.HiRes.isDerivedFrom("Mesh::Feature"): - m = obj.HiRes.Mesh - else: - m = obj.HiRes.Shape - hires = m.copy() - hires.Placement = obj.Placement.multiply(m.Placement) - if not hires: - if hasattr(obj,"CloneOf"): - if obj.CloneOf: - if hasattr(obj.CloneOf,"HiRes"): - if obj.CloneOf.HiRes: - if obj.CloneOf.HiRes.isDerivedFrom("Mesh::Feature"): - m = obj.CloneOf.HiRes.Mesh - else: - m = obj.CloneOf.HiRes.Shape - hires = m.copy() - hires.Placement = obj.Placement.multiply(obj.CloneOf.Placement).multiply(m.Placement) - else: - visible = True - if visible: - if hires: - vlist,vnlist,elist,flist = getIndices(obj,hires,offsetv,offsetvn) - else: - if hasattr(obj,"Shape") and obj.Shape: - vlist,vnlist,elist,flist = getIndices(obj,obj.Shape,offsetv,offsetvn) - elif hasattr(obj,"Mesh") and obj.Mesh: - vlist,vnlist, elist,flist = getIndices(obj,obj.Mesh,offsetv,offsetvn) - if vlist is None: - FreeCAD.Console.PrintError("Unable to export object "+obj.Label+". Skipping.\n") - else: - offsetv += len(vlist) - offsetvn += len(vnlist) - outfile.write("o " + obj.Label + "\n") - - # write material - m = False - if hasattr(obj,"Material"): - if obj.Material: - if hasattr(obj.Material,"Material"): - outfile.write("usemtl " + obj.Material.Name + "\n") - materials.append(obj.Material) - m = True - if not m: - if colors: - if obj.Name in colors: - color = colors[obj.Name] - if color: - if isinstance(color[0],tuple): - # this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors - color = color[0] - #print("found color for obj",obj.Name,":",color) - mn = Draft.getrgb(color,testbw=False)[1:] - outfile.write("usemtl color_" + mn + "\n") - materials.append(("color_" + mn,color,0)) - elif FreeCAD.GuiUp: - if hasattr(obj.ViewObject,"ShapeColor") and hasattr(obj.ViewObject,"Transparency"): - mn = Draft.getrgb(obj.ViewObject.ShapeColor,testbw=False)[1:] - outfile.write("usemtl color_" + mn + "\n") - materials.append(("color_" + mn,obj.ViewObject.ShapeColor,obj.ViewObject.Transparency)) - - # write geometry - for v in vlist: - outfile.write("v" + v + "\n") - for vn in vnlist: - outfile.write("vn" + vn + "\n") - for e in elist: - outfile.write("l" + e + "\n") - for f in flist: - outfile.write("f" + f + "\n") + if FreeCAD.GuiUp: + visible = obj.Visibility + else: + visible = True + if not visible: + continue + + if not (obj.isDerivedFrom("Part::Feature") + or obj.isDerivedFrom("Mesh::Feature") + or obj.isDerivedFrom("App::Link")): + continue + + if obj.isDerivedFrom("App::Link"): + # Get global placement from a Link: + obj_place = obj.Placement + parents = obj.Parents + if parents: + doc = obj.Document + parents = [parents[0][0]] + [doc.getObject(name) for name in parents[0][1].split(".")[:-2]] + parents.reverse() # child-parent-grandparent order. + for parent in parents: + obj_place = parent.Placement.multiply(obj_place) + else: + obj_place = obj.getGlobalPlacement() + + hires = None + if FreeCAD.GuiUp: + if obj.ViewObject.DisplayMode == "HiRes": + # check if high-resolution object is available + if hasattr(obj,"HiRes") and obj.HiRes: + if obj.HiRes.isDerivedFrom("Mesh::Feature"): + hires = obj.HiRes.Mesh.copy() + else: + hires = obj.HiRes.Shape.copy(False, True) + hires.Placement = obj_place.multiply(hires.Placement) + if not hires \ + and hasattr(obj,"CloneOf") \ + and obj.CloneOf \ + and hasattr(obj.CloneOf,"HiRes") \ + and obj.CloneOf.HiRes: + if obj.CloneOf.HiRes.isDerivedFrom("Mesh::Feature"): + hires = obj.CloneOf.HiRes.Mesh.copy() + else: + hires = obj.CloneOf.HiRes.Shape.copy(False, True) + hires.Placement = obj_place.multiply(obj.CloneOf.Placement).multiply(hires.Placement) + + if hires: + vlist,vnlist,elist,flist = getIndices(obj,hires,offsetv,offsetvn) + elif hasattr(obj,"Shape") and obj.Shape: + shape = obj.Shape.copy(False, True) + shape.Placement = obj_place + vlist,vnlist,elist,flist = getIndices(obj,shape,offsetv,offsetvn) + elif hasattr(obj,"Mesh") and obj.Mesh: + mesh = obj.Mesh.copy() + mesh.Placement = obj_place + vlist,vnlist, elist,flist = getIndices(obj,mesh,offsetv,offsetvn) + + if vlist is None: + FreeCAD.Console.PrintError("Unable to export object "+obj.Label+". Skipping.\n") + else: + offsetv += len(vlist) + offsetvn += len(vnlist) + outfile.write("o " + obj.Label + "\n") + + # write material + m = False + if hasattr(obj,"Material"): + if obj.Material: + if hasattr(obj.Material,"Material"): + outfile.write("usemtl " + obj.Material.Name + "\n") + materials.append(obj.Material) + m = True + if not m: + if colors: + if obj.Name in colors: + color = colors[obj.Name] + if color: + if isinstance(color[0],tuple): + # this is a diffusecolor. For now, use the first color - #TODO: Support per-face colors + color = color[0] + #print("found color for obj",obj.Name,":",color) + mn = Draft.getrgb(color,testbw=False)[1:] + outfile.write("usemtl color_" + mn + "\n") + materials.append(("color_" + mn,color,0)) + elif FreeCAD.GuiUp: + if hasattr(obj.ViewObject,"ShapeColor") and hasattr(obj.ViewObject,"Transparency"): + mn = Draft.getrgb(obj.ViewObject.ShapeColor,testbw=False)[1:] + outfile.write("usemtl color_" + mn + "\n") + materials.append(("color_" + mn,obj.ViewObject.ShapeColor,obj.ViewObject.Transparency)) + + # write geometry + for v in vlist: + outfile.write("v" + v + "\n") + for vn in vnlist: + outfile.write("vn" + vn + "\n") + for e in elist: + outfile.write("l" + e + "\n") + for f in flist: + outfile.write("f" + f + "\n") + outfile.close() FreeCAD.Console.PrintMessage(translate("Arch","Successfully written") + " " + filename + "\n") if materials: