Additionally 2 Arch_Window bugs were fixed: * If the W1 value was changed the box tracker was not repositioned relative to the cursor. * The WindowColor was not applied because of a typo in the code. De current default color is quite dark BTW. Note that all dimensional values that were not really defaults, but just the last entered values, have been removed from preferences-archdefaults.ui. As a result the layout looks a bit strange. That will be improved in a next PR.
312 lines
13 KiB
Python
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.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)
|