Arch: added 3DS importer
This commit is contained in:
parent
14d724d722
commit
62cb403125
|
@ -35,6 +35,13 @@ SET(Arch_SRCS
|
||||||
ArchMaterial.py
|
ArchMaterial.py
|
||||||
ArchSchedule.py
|
ArchSchedule.py
|
||||||
ArchProfile.py
|
ArchProfile.py
|
||||||
|
import3DS.py
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(Dice3DS_SRCS
|
||||||
|
Dice3DS/__init__.py
|
||||||
|
Dice3DS/util.py
|
||||||
|
Dice3DS/dom3ds.py
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(Arch_presets
|
SET(Arch_presets
|
||||||
|
@ -48,6 +55,7 @@ ADD_CUSTOM_TARGET(Arch ALL
|
||||||
)
|
)
|
||||||
|
|
||||||
fc_copy_sources(Arch "${CMAKE_BINARY_DIR}/Mod/Arch" ${Arch_SRCS})
|
fc_copy_sources(Arch "${CMAKE_BINARY_DIR}/Mod/Arch" ${Arch_SRCS})
|
||||||
|
fc_copy_sources(Arch "${CMAKE_BINARY_DIR}/Mod/Arch/Dice3DS" ${Dice3DS_SRCS})
|
||||||
|
|
||||||
fc_target_copy_resource(Arch
|
fc_target_copy_resource(Arch
|
||||||
${CMAKE_SOURCE_DIR}/src/Mod/Arch
|
${CMAKE_SOURCE_DIR}/src/Mod/Arch
|
||||||
|
@ -69,6 +77,12 @@ INSTALL(
|
||||||
DESTINATION Mod/Arch
|
DESTINATION Mod/Arch
|
||||||
)
|
)
|
||||||
|
|
||||||
|
INSTALL(
|
||||||
|
FILES
|
||||||
|
${Dice3DS_SRCS}
|
||||||
|
DESTINATION Mod/Arch/Dice3DS
|
||||||
|
)
|
||||||
|
|
||||||
INSTALL(
|
INSTALL(
|
||||||
DIRECTORY
|
DIRECTORY
|
||||||
Presets
|
Presets
|
||||||
|
|
3
src/Mod/Arch/Dice3DS/__init__.py
Normal file
3
src/Mod/Arch/Dice3DS/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# __init__.py
|
||||||
|
__all__ = [ 'dom3ds', 'util' ]
|
||||||
|
version = (0,13)
|
1908
src/Mod/Arch/Dice3DS/dom3ds.py
Normal file
1908
src/Mod/Arch/Dice3DS/dom3ds.py
Normal file
File diff suppressed because it is too large
Load Diff
272
src/Mod/Arch/Dice3DS/util.py
Normal file
272
src/Mod/Arch/Dice3DS/util.py
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
# util.py
|
||||||
|
|
||||||
|
"""Utitily function for Dice3DS.
|
||||||
|
|
||||||
|
Defines some routines for calculating normals and transforming points.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
|
||||||
|
# Can push numpy.float64 (or even numpy.float80) into this if you
|
||||||
|
# would like to use higher precision when calculating; results will be
|
||||||
|
# converted back to numpy.float32
|
||||||
|
_calc_precision_type = numpy.float32
|
||||||
|
|
||||||
|
|
||||||
|
def translate_points(pointarray,matrix):
|
||||||
|
"""Translate points in pointarray by the given matrix.
|
||||||
|
|
||||||
|
tpointarray = translate_points(pointarray,matrix)
|
||||||
|
|
||||||
|
Takes array of points and a homogenous (4D) transformation
|
||||||
|
matrix in exactly the same form in which they appear in the
|
||||||
|
3DS DOM.
|
||||||
|
|
||||||
|
Returns a pointarray with the points transformed by the matrix.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
n = len(pointarray)
|
||||||
|
pt = numpy.ones((n,4),_calc_precision_type)
|
||||||
|
pt[:,:3] = pointarray
|
||||||
|
tpt = numpy.transpose(numpy.dot(matrix,numpy.transpose(pt)))
|
||||||
|
return numpy.asarray(tpt[:,:3]/tpt[:,3:4],numpy.float32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_normals_no_smoothing(pointarray,facearray,smarray=None):
|
||||||
|
"""Calculate normals all perpendicular to the faces.
|
||||||
|
|
||||||
|
points,norms = calculate_normals_no_smoothing(
|
||||||
|
pointarray,facearray,smarray=None)
|
||||||
|
|
||||||
|
Takes an array of points and faces in exactly the same form in
|
||||||
|
which they appear in the 3DS DOM. It accepts a smoothing array,
|
||||||
|
but ignores it.
|
||||||
|
|
||||||
|
Returns a numpy.array of points, one per row, and a
|
||||||
|
numpy.array of the corresponding normals. The points are
|
||||||
|
returned as a list of consecutive triangles; the first three rows
|
||||||
|
make up the first triangle, the second three rows make up the
|
||||||
|
second triangle, and so on.
|
||||||
|
|
||||||
|
The normal vectors are determined by calculating the normal to
|
||||||
|
each face. There is no smoothing.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# prepare to calculate normals. define some arrays
|
||||||
|
|
||||||
|
m = len(facearray)
|
||||||
|
fnorms = numpy.empty((m*3,3),_calc_precision_type)
|
||||||
|
points = pointarray[facearray.ravel()]
|
||||||
|
|
||||||
|
# calculate normals for each face
|
||||||
|
|
||||||
|
A = numpy.asarray(pointarray[facearray[:,0]],_calc_precision_type)
|
||||||
|
B = numpy.asarray(pointarray[facearray[:,1]],_calc_precision_type)
|
||||||
|
C = numpy.asarray(pointarray[facearray[:,2]],_calc_precision_type)
|
||||||
|
b = A - C
|
||||||
|
c = B - A
|
||||||
|
fnorms[2::3,0] = c[:,2]*b[:,1]-c[:,1]*b[:,2]
|
||||||
|
fnorms[2::3,1] = c[:,0]*b[:,2]-c[:,2]*b[:,0]
|
||||||
|
fnorms[2::3,2] = c[:,1]*b[:,0]-c[:,0]*b[:,1]
|
||||||
|
a = fnorms[2::3]
|
||||||
|
q = numpy.maximum(numpy.sqrt(numpy.sum(a*a,axis=1)),1e-10)
|
||||||
|
q = q[:,numpy.newaxis]
|
||||||
|
a /= q
|
||||||
|
fnorms[0::3] = fnorms[1::3] = fnorms[2::3]
|
||||||
|
|
||||||
|
# we're done
|
||||||
|
|
||||||
|
return points, numpy.asarray(fnorms,numpy.float32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_normals_by_cross_product(pointarray,facearray,smarray):
|
||||||
|
"""Calculate normals by smoothing, weighting by cross-product.
|
||||||
|
|
||||||
|
points,norms = calculate_normals_by_cross_product(
|
||||||
|
pointarray,facearray,smarray)
|
||||||
|
|
||||||
|
Takes an array of points, faces, and a smoothing group in exactly
|
||||||
|
the same form in which they appear in the 3DS DOM.
|
||||||
|
|
||||||
|
Returns a numpy.array of points, one per row, and a numpy.array of
|
||||||
|
the corresponding normals. The points are returned as a list of
|
||||||
|
consecutive triangles; the first three rows make up the first
|
||||||
|
triangle, the second three rows make up the second triangle, and
|
||||||
|
so on.
|
||||||
|
|
||||||
|
To calculate the normal of a given vertex on a given face, this
|
||||||
|
function averages the normal vector for all faces which have share
|
||||||
|
that vertex and a smoothing group.
|
||||||
|
|
||||||
|
The normals being averaged are weighted by the cross-product used
|
||||||
|
to obtain the face's normal, which is proportional to the area of
|
||||||
|
the face.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# prepare to calculate normals. define some arrays
|
||||||
|
|
||||||
|
m = len(facearray)
|
||||||
|
rnorms = numpy.zeros((m*3,3),_calc_precision_type)
|
||||||
|
fnorms = numpy.zeros((m*3,3),_calc_precision_type)
|
||||||
|
points = pointarray[facearray.ravel()]
|
||||||
|
exarray = numpy.zeros(3*m,numpy.uint32)
|
||||||
|
if smarray is not None:
|
||||||
|
exarray[0::3] = exarray[1::3] = exarray[2::3] = smarray
|
||||||
|
|
||||||
|
# calculate scaled normals (according to angle subtended)
|
||||||
|
|
||||||
|
A = numpy.asarray(pointarray[facearray[:,0]],_calc_precision_type)
|
||||||
|
B = numpy.asarray(pointarray[facearray[:,1]],_calc_precision_type)
|
||||||
|
C = numpy.asarray(pointarray[facearray[:,2]],_calc_precision_type)
|
||||||
|
a = C - B
|
||||||
|
b = A - C
|
||||||
|
c = B - A
|
||||||
|
rnorms[0::3,0] = c[:,2]*b[:,1]-c[:,1]*b[:,2]
|
||||||
|
rnorms[0::3,1] = c[:,0]*b[:,2]-c[:,2]*b[:,0]
|
||||||
|
rnorms[0::3,2] = c[:,1]*b[:,0]-c[:,0]*b[:,1]
|
||||||
|
rnorms[1::3,0] = a[:,2]*c[:,1]-a[:,1]*c[:,2]
|
||||||
|
rnorms[1::3,1] = a[:,0]*c[:,2]-a[:,2]*c[:,0]
|
||||||
|
rnorms[1::3,2] = a[:,1]*c[:,0]-a[:,0]*c[:,1]
|
||||||
|
rnorms[2::3,0] = b[:,2]*a[:,1]-b[:,1]*a[:,2]
|
||||||
|
rnorms[2::3,1] = b[:,0]*a[:,2]-b[:,2]*a[:,0]
|
||||||
|
rnorms[2::3,2] = b[:,1]*a[:,0]-b[:,0]*a[:,1]
|
||||||
|
|
||||||
|
# normalize vectors according to passed in smoothing group
|
||||||
|
|
||||||
|
lex = numpy.lexsort(numpy.transpose(points))
|
||||||
|
brs = numpy.nonzero(
|
||||||
|
numpy.any(points[lex[1:],:]-points[lex[:-1],:],axis=1))[0]+1
|
||||||
|
lslice = numpy.empty((len(brs)+1,),numpy.int)
|
||||||
|
lslice[0] = 0
|
||||||
|
lslice[1:] = brs
|
||||||
|
rslice = numpy.empty((len(brs)+1,),numpy.int)
|
||||||
|
rslice[:-1] = brs
|
||||||
|
rslice[-1] = 3*m
|
||||||
|
for i in xrange(len(brs)+1):
|
||||||
|
rgroup = lex[lslice[i]:rslice[i]]
|
||||||
|
xgroup = exarray[rgroup]
|
||||||
|
normpat = numpy.logical_or(
|
||||||
|
numpy.bitwise_and.outer(xgroup,xgroup),
|
||||||
|
numpy.eye(len(xgroup)))
|
||||||
|
fnorms[rgroup,:] = numpy.dot(normpat,rnorms[rgroup,:])
|
||||||
|
q = numpy.sum(fnorms*fnorms,axis=1)
|
||||||
|
qnz = numpy.nonzero(q)[0]
|
||||||
|
lq = 1.0 / numpy.sqrt(q[qnz])
|
||||||
|
fnt = numpy.transpose(fnorms)
|
||||||
|
fnt[:,qnz] *= lq
|
||||||
|
|
||||||
|
# we're done
|
||||||
|
|
||||||
|
return points, numpy.asarray(fnorms,numpy.float32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_normals_by_angle_subtended(pointarray,facearray,smarray):
|
||||||
|
"""Calculate normals by smoothing, weighting by angle subtended.
|
||||||
|
|
||||||
|
points,norms = calculate_normals_by_angle_subtended(
|
||||||
|
pointarray,facearray,smarray)
|
||||||
|
|
||||||
|
Takes an array of points, faces, and a smoothing group in exactly
|
||||||
|
the same form in which they appear in the 3DS DOM.
|
||||||
|
|
||||||
|
Returns a numpy.array of points, one per row, and a numpy.array of
|
||||||
|
the corresponding normals. The points are returned as a list of
|
||||||
|
consecutive triangles; the first three rows make up the first
|
||||||
|
triangle, the second three rows make up the second triangle, and
|
||||||
|
so on.
|
||||||
|
|
||||||
|
To calculate the normal of a given vertex on a given face, this
|
||||||
|
function averages the normal vector for all faces which have share
|
||||||
|
that vertex, and a smoothing group.
|
||||||
|
|
||||||
|
The normals being averaged are weighted by the angle subtended.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# prepare to calculate normals. define some arrays
|
||||||
|
|
||||||
|
m = len(facearray)
|
||||||
|
rnorms = numpy.zeros((m*3,3),_calc_precision_type)
|
||||||
|
fnorms = numpy.zeros((m*3,3),_calc_precision_type)
|
||||||
|
points = pointarray[facearray.ravel()]
|
||||||
|
exarray = numpy.zeros(3*m,numpy.uint32)
|
||||||
|
if smarray is not None:
|
||||||
|
exarray[0::3] = exarray[1::3] = exarray[2::3] = smarray
|
||||||
|
|
||||||
|
# weed out degenerate triangles first
|
||||||
|
# unlike cross-product, angle subtended blows up on degeneracy
|
||||||
|
|
||||||
|
A = numpy.asarray(pointarray[facearray[:,0]],_calc_precision_type)
|
||||||
|
B = numpy.asarray(pointarray[facearray[:,1]],_calc_precision_type)
|
||||||
|
C = numpy.asarray(pointarray[facearray[:,2]],_calc_precision_type)
|
||||||
|
a = C - B
|
||||||
|
b = A - C
|
||||||
|
c = B - A
|
||||||
|
p = numpy.zeros((len(facearray),3),_calc_precision_type)
|
||||||
|
p[:,0] = c[:,2]*b[:,1]-c[:,1]*b[:,2]
|
||||||
|
p[:,1] = c[:,0]*b[:,2]-c[:,2]*b[:,0]
|
||||||
|
p[:,2] = c[:,1]*b[:,0]-c[:,0]*b[:,1]
|
||||||
|
aa = numpy.sum(a*a,axis=1)
|
||||||
|
bb = numpy.sum(b*b,axis=1)
|
||||||
|
cc = numpy.sum(c*c,axis=1)
|
||||||
|
pp = numpy.sum(p*p,axis=1)
|
||||||
|
ndg = numpy.nonzero(numpy.logical_and.reduce((aa,bb,cc,pp)))[0]
|
||||||
|
|
||||||
|
# calculate scaled normals (according to angle subtended)
|
||||||
|
|
||||||
|
p = p[ndg]
|
||||||
|
la = numpy.sqrt(aa[ndg])
|
||||||
|
lb = numpy.sqrt(bb[ndg])
|
||||||
|
lc = numpy.sqrt(cc[ndg])
|
||||||
|
lp = numpy.sqrt(pp[ndg])
|
||||||
|
sinA = numpy.clip(lp/lb/lc,-1.0,1.0)
|
||||||
|
sinB = numpy.clip(lp/la/lc,-1.0,1.0)
|
||||||
|
sinC = numpy.clip(lp/la/lb,-1.0,1.0)
|
||||||
|
sinA2 = sinA*sinA
|
||||||
|
sinB2 = sinB*sinB
|
||||||
|
sinC2 = sinC*sinC
|
||||||
|
angA = numpy.arcsin(sinA)
|
||||||
|
angB = numpy.arcsin(sinB)
|
||||||
|
angC = numpy.arcsin(sinC)
|
||||||
|
angA = numpy.where(sinA2 > sinB2 + sinC2, numpy.pi - angA, angA)
|
||||||
|
angB = numpy.where(sinB2 > sinA2 + sinC2, numpy.pi - angB, angB)
|
||||||
|
angC = numpy.where(sinC2 > sinA2 + sinB2, numpy.pi - angC, angC)
|
||||||
|
rnorms[0::3][ndg] = p*(angA/lp)[:,numpy.newaxis]
|
||||||
|
rnorms[1::3][ndg] = p*(angB/lp)[:,numpy.newaxis]
|
||||||
|
rnorms[2::3][ndg] = p*(angC/lp)[:,numpy.newaxis]
|
||||||
|
|
||||||
|
# normalize vectors according to passed in smoothing group
|
||||||
|
|
||||||
|
lex = numpy.lexsort(numpy.transpose(points))
|
||||||
|
brs = numpy.nonzero(
|
||||||
|
numpy.any(points[lex[1:],:]-points[lex[:-1],:],axis=1))[0]+1
|
||||||
|
lslice = numpy.empty((len(brs)+1,),numpy.int)
|
||||||
|
lslice[0] = 0
|
||||||
|
lslice[1:] = brs
|
||||||
|
rslice = numpy.empty((len(brs)+1,),numpy.int)
|
||||||
|
rslice[:-1] = brs
|
||||||
|
rslice[-1] = 3*m
|
||||||
|
for i in xrange(len(brs)+1):
|
||||||
|
rgroup = lex[lslice[i]:rslice[i]]
|
||||||
|
xgroup = exarray[rgroup]
|
||||||
|
normpat = numpy.logical_or(
|
||||||
|
numpy.bitwise_and.outer(xgroup,xgroup),
|
||||||
|
numpy.eye(len(xgroup)))
|
||||||
|
fnorms[rgroup,:] = numpy.dot(normpat,rnorms[rgroup,:])
|
||||||
|
q = numpy.sum(fnorms*fnorms,axis=1)
|
||||||
|
qnz = numpy.nonzero(q)[0]
|
||||||
|
lq = 1.0 / numpy.sqrt(q[qnz])
|
||||||
|
fnt = numpy.transpose(fnorms)
|
||||||
|
fnt[:,qnz] *= lq
|
||||||
|
|
||||||
|
# we're done
|
||||||
|
|
||||||
|
return points, numpy.asarray(fnorms,numpy.float32)
|
|
@ -28,3 +28,4 @@ FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ")
|
||||||
FreeCAD.addExportType("WebGL file (*.html)","importWebGL")
|
FreeCAD.addExportType("WebGL file (*.html)","importWebGL")
|
||||||
FreeCAD.addImportType("Collada (*.dae)","importDAE")
|
FreeCAD.addImportType("Collada (*.dae)","importDAE")
|
||||||
FreeCAD.addExportType("Collada (*.dae)","importDAE")
|
FreeCAD.addExportType("Collada (*.dae)","importDAE")
|
||||||
|
FreeCAD.addImportType("3D Studio mesh (*.3ds)","import3DS")
|
||||||
|
|
105
src/Mod/Arch/import3DS.py
Normal file
105
src/Mod/Arch/import3DS.py
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#***************************************************************************
|
||||||
|
#* *
|
||||||
|
#* Copyright (c) 2016 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 os,FreeCAD,Mesh
|
||||||
|
|
||||||
|
__title__="FreeCAD 3DS importer"
|
||||||
|
__author__ = "Yorik van Havre"
|
||||||
|
__url__ = "http://www.freecadweb.org"
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
|
def check3DS():
|
||||||
|
"checks if collada if available"
|
||||||
|
global dom3ds
|
||||||
|
dom3ds = None
|
||||||
|
try:
|
||||||
|
from Dice3DS import dom3ds
|
||||||
|
except ImportError:
|
||||||
|
FreeCAD.Console.PrintError("Dice3DS not found, 3DS support is disabled.\n")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def open(filename):
|
||||||
|
"called when freecad wants to open a file"
|
||||||
|
if not check3DS():
|
||||||
|
return
|
||||||
|
docname = (os.path.splitext(os.path.basename(filename))[0]).encode("utf8")
|
||||||
|
doc = FreeCAD.newDocument(docname)
|
||||||
|
doc.Label = decode(docname)
|
||||||
|
FreeCAD.ActiveDocument = doc
|
||||||
|
read(filename)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
def insert(filename,docname):
|
||||||
|
"called when freecad wants to import a file"
|
||||||
|
if not check3DS():
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
doc = FreeCAD.getDocument(docname)
|
||||||
|
except NameError:
|
||||||
|
doc = FreeCAD.newDocument(docname)
|
||||||
|
FreeCAD.ActiveDocument = doc
|
||||||
|
read(filename)
|
||||||
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
def decode(name):
|
||||||
|
"decodes encoded strings"
|
||||||
|
try:
|
||||||
|
decodedName = (name.decode("utf8"))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
try:
|
||||||
|
decodedName = (name.decode("latin1"))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
FreeCAD.Console.PrintError(translate("Arch","Error: Couldn't determine character encoding"))
|
||||||
|
decodedName = name
|
||||||
|
return decodedName
|
||||||
|
|
||||||
|
|
||||||
|
def read(filename):
|
||||||
|
dom = dom3ds.read_3ds_file(filename,tight=False)
|
||||||
|
|
||||||
|
for j,d_nobj in enumerate(dom.mdata.objects):
|
||||||
|
if type(d_nobj.obj) != dom3ds.N_TRI_OBJECT:
|
||||||
|
continue
|
||||||
|
verts = []
|
||||||
|
if d_nobj.obj.points:
|
||||||
|
for d_point in d_nobj.obj.points.array:
|
||||||
|
verts.append([d_point[0],d_point[1],d_point[2]])
|
||||||
|
meshdata = []
|
||||||
|
for d_face in d_nobj.obj.faces.array:
|
||||||
|
meshdata.append([verts[int(d_face[i])] for i in xrange(3)])
|
||||||
|
m = [tuple(r) for r in d_nobj.obj.matrix.array]
|
||||||
|
m = m[0] + m[1] + m[2] + m[3]
|
||||||
|
placement = FreeCAD.Placement(FreeCAD.Matrix(*m))
|
||||||
|
mesh = Mesh.Mesh(meshdata)
|
||||||
|
obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature","Mesh")
|
||||||
|
obj.Mesh = mesh
|
||||||
|
obj.Placement = placement
|
||||||
|
else:
|
||||||
|
print "Skipping object without vertices array: ",d_nobj.obj
|
||||||
|
|
Loading…
Reference in New Issue
Block a user