Files
create/src/Mod/Arch/importDAE.py

312 lines
13 KiB
Python

#***************************************************************************
#* Copyright (c) 2011 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD, Mesh, os, numpy, MeshPart, Arch, Draft
from draftutils import params
if FreeCAD.GuiUp:
from draftutils.translate import translate
else:
# \cond
def translate(context,text):
return text
# \endcond
## @package importDAE
# \ingroup ARCH
# \brief DAE (Collada) file format importer and exporter
#
# This module provides tools to import and export Collada (.dae) files.
__title__ = "FreeCAD Collada importer"
__author__ = "Yorik van Havre"
__url__ = "https://www.freecad.org"
DEBUG = True
try:
# Python 2 forward compatibility
range = xrange
except NameError:
pass
def checkCollada():
"checks if collada if available"
global collada
COLLADA = None
try:
import collada
except ImportError:
FreeCAD.Console.PrintError(translate("Arch","pycollada not found, collada support is disabled.")+"\n")
return False
else:
return True
def triangulate(shape):
"triangulates the given face"
mesher = params.get_param_arch("ColladaMesher")
tessellation = params.get_param_arch("ColladaTessellation")
grading = params.get_param_arch("ColladaGrading")
segsperedge = params.get_param_arch("ColladaSegsPerEdge")
segsperradius = params.get_param_arch("ColladaSegsPerRadius")
secondorder = params.get_param_arch("ColladaSecondOrder")
optimize = params.get_param_arch("ColladaOptimize")
allowquads = params.get_param_arch("ColladaAllowQuads")
if mesher == 0:
return shape.tessellate(tessellation)
elif mesher == 1:
return MeshPart.meshFromShape(Shape=shape,MaxLength=tessellation).Topology
else:
return MeshPart.meshFromShape(Shape=shape,GrowthRate=grading,SegPerEdge=segsperedge,
SegPerRadius=segsperradius,SecondOrder=secondorder,Optimize=optimize,
AllowQuad=allowquads).Topology
def open(filename):
"called when freecad wants to open a file"
if not checkCollada():
return
docname = os.path.splitext(os.path.basename(filename))[0]
doc = FreeCAD.newDocument(docname)
doc.Label = docname
FreeCAD.ActiveDocument = doc
read(filename)
return doc
def insert(filename,docname):
"called when freecad wants to import a file"
if not checkCollada():
return
try:
doc = FreeCAD.getDocument(docname)
except NameError:
doc = FreeCAD.newDocument(docname)
FreeCAD.ActiveDocument = doc
read(filename)
return doc
def read(filename):
"reads a DAE file"
global col
col = collada.Collada(filename, ignore=[collada.common.DaeUnsupportedError])
# Read the unitmeter info from dae file and compute unit to convert to mm
unitmeter = col.assetInfo.unitmeter or 1
unit = unitmeter / 0.001
#for geom in col.geometries:
#for geom in col.scene.objects('geometry'):
for node in col.scene.nodes:
if list(node.objects("geometry")):
color = None
# retrieving material
if "}" in node.xmlnode.tag:
bt = node.xmlnode.tag.split("}")[0]+"}"
gnode = node.xmlnode.find(bt+"instance_geometry")
if gnode is not None:
bnode = gnode.find(bt+"bind_material")
if bnode is not None:
tnode = bnode.find(bt+"technique_common")
if tnode is not None:
mnode = tnode.find(bt+"instance_material")
if mnode is not None:
if "target" in mnode:
mname = mnode.get("target").strip("#")
for m in col.materials:
if m.id == mname:
e = m.effect
if isinstance(e.diffuse,tuple):
color = e.diffuse
for geom in node.objects("geometry"):
for prim in geom.primitives():
#print(prim, dir(prim))
meshdata = []
if hasattr(prim,"triangles"):
tset = prim.triangles()
elif hasattr(prim,"triangleset"):
tset = prim.triangleset()
else:
tset = []
for tri in tset:
face = []
for v in tri.vertices:
v = [x * unit for x in v]
face.append([v[0],v[1],v[2]])
meshdata.append(face)
#print(meshdata)
newmesh = Mesh.Mesh(meshdata)
#print(newmesh)
obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature","Mesh")
obj.Mesh = newmesh
if color and FreeCAD.GuiUp:
obj.ViewObject.ShapeColor = color
def export(exportList,filename,tessellation=1,colors=None):
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
colors is an optional dictionary of objName:shapeColorTuple or objName:diffuseColorList elements
to be used in non-GUI mode if you want to be able to export colors. Tessellation is used when breaking
curved surfaces into triangles."""
if not checkCollada(): return
scale = params.get_param_arch("ColladaScalingFactor")
scale = scale * 0.001 # from millimeters (FreeCAD) to meters (Collada)
defaultcolor = Draft.get_rgba_tuple(params.get_param_view("DefaultShapeColor"))[:3]
colmesh = collada.Collada()
colmesh.assetInfo.upaxis = collada.asset.UP_AXIS.Z_UP
# authoring info
cont = collada.asset.Contributor()
try:
author = FreeCAD.ActiveDocument.CreatedBy
except UnicodeEncodeError:
author = FreeCAD.ActiveDocument.CreatedBy.encode("utf8")
author = author.replace("<","")
author = author.replace(">","")
cont.author = author
ver = FreeCAD.Version()
appli = "FreeCAD v" + ver[0] + "." + ver[1] + " build" + ver[2] + "\n"
cont.authoring_tool = appli
#print(author, appli)
colmesh.assetInfo.contributors.append(cont)
colmesh.assetInfo.unitname = "meter"
colmesh.assetInfo.unitmeter = 1.0
defaultmat = None
objind = 0
scenenodes = []
objectslist = Draft.get_group_contents(exportList, walls=True,
addgroups=True)
objectslist = Arch.pruneIncluded(objectslist)
for obj in objectslist:
findex = numpy.array([])
m = None
if obj.isDerivedFrom("Part::Feature"):
print("exporting object ",obj.Name, obj.Shape)
new_shape = obj.Shape.copy()
new_shape.Placement = obj.getGlobalPlacement()
m = Mesh.Mesh(triangulate(new_shape))
elif obj.isDerivedFrom("Mesh::Feature"):
print("exporting object ",obj.Name, obj.Mesh)
m = obj.Mesh
elif obj.isDerivedFrom("App::Part"):
for child in obj.OutList:
objectslist.append(child)
continue
else:
continue
if m:
Topology = m.Topology
Facets = m.Facets
# vertex indices
vindex = numpy.empty(len(Topology[0]) * 3)
for i in range(len(Topology[0])):
v = Topology[0][i]
vindex[list(range(i*3, i*3+3))] = (v.x*scale,v.y*scale,v.z*scale)
# normals
nindex = numpy.empty(len(Facets) * 3)
for i in range(len(Facets)):
n = Facets[i].Normal
nindex[list(range(i*3, i*3+3))] = (n.x,n.y,n.z)
# face indices
findex = numpy.empty(len(Topology[1]) * 6, numpy.int64)
for i in range(len(Topology[1])):
f = Topology[1][i]
findex[list(range(i*6, i*6+6))] = (f[0],i,f[1],i,f[2],i)
print(len(vindex), " vert indices, ", len(nindex), " norm indices, ", len(findex), " face indices.")
vert_src = collada.source.FloatSource("cubeverts-array"+str(objind), vindex, ('X', 'Y', 'Z'))
normal_src = collada.source.FloatSource("cubenormals-array"+str(objind), nindex, ('X', 'Y', 'Z'))
geom = collada.geometry.Geometry(colmesh, "geometry"+str(objind), obj.Name, [vert_src, normal_src])
input_list = collada.source.InputList()
input_list.addInput(0, 'VERTEX', "#cubeverts-array"+str(objind))
input_list.addInput(1, 'NORMAL', "#cubenormals-array"+str(objind))
matnode = None
matref = "materialref"
if hasattr(obj,"Material"):
if obj.Material:
if hasattr(obj.Material,"Material"):
if "DiffuseColor" in obj.Material.Material:
kd = tuple([float(k) for k in obj.Material.Material["DiffuseColor"].strip("()").split(",")])
effect = collada.material.Effect("effect_"+obj.Material.Name, [], "phong", diffuse=kd, specular=(1,1,1))
mat = collada.material.Material("mat_"+obj.Material.Name, obj.Material.Name, effect)
colmesh.effects.append(effect)
colmesh.materials.append(mat)
matref = "ref_"+obj.Material.Name
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
if not matnode:
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)
kd = color[:3]
effect = collada.material.Effect("effect_"+obj.Name, [], "phong", diffuse=kd, specular=(1,1,1))
mat = collada.material.Material("mat_"+obj.Name, obj.Name, effect)
colmesh.effects.append(effect)
colmesh.materials.append(mat)
matref = "ref_"+obj.Name
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
elif FreeCAD.GuiUp:
if hasattr(obj.ViewObject,"ShapeColor"):
kd = obj.ViewObject.ShapeColor[:3]
effect = collada.material.Effect("effect_"+obj.Name, [], "phong", diffuse=kd, specular=(1,1,1))
mat = collada.material.Material("mat_"+obj.Name, obj.Name, effect)
colmesh.effects.append(effect)
colmesh.materials.append(mat)
matref = "ref_"+obj.Name
matnode = collada.scene.MaterialNode(matref, mat, inputs=[])
if not matnode:
if not defaultmat:
effect = collada.material.Effect("effect_default", [], "phong", diffuse=defaultcolor, specular=(1,1,1))
defaultmat = collada.material.Material("mat_default", "default_material", effect)
colmesh.effects.append(effect)
colmesh.materials.append(defaultmat)
matnode = collada.scene.MaterialNode(matref, defaultmat, inputs=[])
triset = geom.createTriangleSet(findex, input_list, matref)
geom.primitives.append(triset)
colmesh.geometries.append(geom)
geomnode = collada.scene.GeometryNode(geom, [matnode])
node = collada.scene.Node("node"+str(objind), children=[geomnode])
scenenodes.append(node)
objind += 1
myscene = collada.scene.Scene("myscene", scenenodes)
colmesh.scenes.append(myscene)
colmesh.scene = myscene
colmesh.write(filename)
FreeCAD.Console.PrintMessage(translate("Arch","file %s successfully created.") % filename)