Skip to content

Instantly share code, notes, and snippets.

@jonnysp
Last active July 16, 2019 10:22
Show Gist options
  • Save jonnysp/15d3e4f4e641231a4649352b8ad4e020 to your computer and use it in GitHub Desktop.
Save jonnysp/15d3e4f4e641231a4649352b8ad4e020 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
#
# pyldd2obj Version 0.4.8 - Copyright (c) 2019 by jonnysp
#
# License: MIT License
#
import os
import sys
import math
import struct
import zipfile
from xml.dom import minidom
import time
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf-8')
PRIMITIVEPATH = '/Primitives/'
GEOMETRIEPATH = PRIMITIVEPATH + 'LOD0/'
DECORATIONPATH = '/Decorations/'
MATERIALNAMESPATH = '/MaterialNames/'
class Matrix3D:
def __init__(self, n11=1,n12=0,n13=0,n14=0,n21=0,n22=1,n23=0,n24=0,n31=0,n32=0,n33=1,n34=0,n41=0,n42=0,n43=0,n44=1):
self.n11 = n11
self.n12 = n12
self.n13 = n13
self.n14 = n14
self.n21 = n21
self.n22 = n22
self.n23 = n23
self.n24 = n24
self.n31 = n31
self.n32 = n32
self.n33 = n33
self.n34 = n34
self.n41 = n41
self.n42 = n42
self.n43 = n43
self.n44 = n44
def __str__(self):
return '[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}]'.format(self.n11, self.n12, self.n13,self.n14,self.n21, self.n22, self.n23,self.n24,self.n31, self.n32, self.n33,self.n34,self.n41, self.n42, self.n43,self.n44)
def rotate(self,angle=0,axis=0):
c = math.cos(angle)
s = math.sin(angle)
t = 1 - c
tx = t * axis.x
ty = t * axis.y
tz = t * axis.z
sx = s * axis.x
sy = s * axis.y
sz = s * axis.z
self.n11 = c + axis.x * tx
self.n12 = axis.y * tx + sz
self.n13 = axis.z * tx - sy
self.n14 = 0
self.n21 = axis.x * ty - sz
self.n22 = c + axis.y * ty
self.n23 = axis.z * ty + sx
self.n24 = 0
self.n31 = axis.x * tz + sy
self.n32 = axis.y * tz - sx
self.n33 = c + axis.z * tz
self.n34 = 0
self.n41 = 0
self.n42 = 0
self.n43 = 0
self.n44 = 1
def __mul__(self, other):
return Matrix3D(
self.n11 * other.n11 + self.n21 * other.n12 + self.n31 * other.n13 + self.n41 * other.n14,
self.n12 * other.n11 + self.n22 * other.n12 + self.n32 * other.n13 + self.n42 * other.n14,
self.n13 * other.n11 + self.n23 * other.n12 + self.n33 * other.n13 + self.n43 * other.n14,
self.n14 * other.n11 + self.n24 * other.n12 + self.n34 * other.n13 + self.n44 * other.n14,
self.n11 * other.n21 + self.n21 * other.n22 + self.n31 * other.n23 + self.n41 * other.n24,
self.n12 * other.n21 + self.n22 * other.n22 + self.n32 * other.n23 + self.n42 * other.n24,
self.n13 * other.n21 + self.n23 * other.n22 + self.n33 * other.n23 + self.n43 * other.n24,
self.n14 * other.n21 + self.n24 * other.n22 + self.n34 * other.n23 + self.n44 * other.n24,
self.n11 * other.n31 + self.n21 * other.n32 + self.n31 * other.n33 + self.n41 * other.n34,
self.n12 * other.n31 + self.n22 * other.n32 + self.n32 * other.n33 + self.n42 * other.n34,
self.n13 * other.n31 + self.n23 * other.n32 + self.n33 * other.n33 + self.n43 * other.n34,
self.n14 * other.n31 + self.n24 * other.n32 + self.n34 * other.n33 + self.n44 * other.n34,
self.n11 * other.n41 + self.n21 * other.n42 + self.n31 * other.n43 + self.n41 * other.n44,
self.n12 * other.n41 + self.n22 * other.n42 + self.n32 * other.n43 + self.n42 * other.n44,
self.n13 * other.n41 + self.n23 * other.n42 + self.n33 * other.n43 + self.n43 * other.n44,
self.n14 * other.n41 + self.n24 * other.n42 + self.n34 * other.n43 + self.n44 * other.n44
)
class Point3D:
def __init__(self, x=0,y=0,z=0):
self.x = x
self.y = y
self.z = z
def __str__(self):
return '[{0},{1},{2}]'.format(self.x, self.y,self.z)
def string(self,prefix = "v"):
return '{0} {1:f} {2:f} {3:f}\n'.format(prefix ,self.x , self.y, self.z)
def transformW(self,matrix):
x = matrix.n11 * self.x + matrix.n21 * self.y + matrix.n31 * self.z
y = matrix.n12 * self.x + matrix.n22 * self.y + matrix.n32 * self.z
z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z
self.x = x
self.y = y
self.z = z
def transform(self,matrix):
x = matrix.n11 * self.x + matrix.n21 * self.y + matrix.n31 * self.z + matrix.n41
y = matrix.n12 * self.x + matrix.n22 * self.y + matrix.n32 * self.z + matrix.n42
z = matrix.n13 * self.x + matrix.n23 * self.y + matrix.n33 * self.z + matrix.n43
self.x = x
self.y = y
self.z = z
def copy(self):
return Point3D(x=self.x,y=self.y,z=self.z)
class Point2D:
def __init__(self, x=0,y=0):
self.x = x
self.y = y
def __str__(self):
return '[{0},{1}]'.format(self.x, self.y * -1)
def string(self,prefix="t"):
return '{0} {1:f} {2:f}\n'.format(prefix , self.x, self.y * -1 )
def copy(self):
return Point2D(x=self.x,y=self.y)
class Face:
def __init__(self,a=0,b=0,c=0):
self.a = a
self.b = b
self.c = c
def string(self,prefix="f", indexOffset=0 ,textureoffset=0):
if textureoffset == 0:
return prefix + ' {0}//{0} {1}//{1} {2}//{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset)
else:
return prefix + ' {0}/{3}/{0} {1}/{4}/{1} {2}/{5}/{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset,self.a + textureoffset, self.b + textureoffset, self.c + textureoffset)
def __str__(self):
return '[{0},{1},{2}]'.format(self.a, self.b, self.c)
class Group:
def __init__(self, node):
self.partRefs = node.getAttribute('partRefs').split(',')
class Bone:
def __init__(self, node):
self.refID = node.getAttribute('refID')
(a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(','))
self.matrix = Matrix3D(n11=a,n12=b,n13=c,n14=0,n21=d,n22=e,n23=f,n24=0,n31=g,n32=h,n33=i,n34=0,n41=x,n42=y,n43=z,n44=1)
class Part:
def __init__(self, node):
self.isGrouped = False
self.GroupIDX = 0
self.Bones = []
self.refID = node.getAttribute('refID')
self.designID = node.getAttribute('designID')
self.materials = list(map(str, node.getAttribute('materials').split(',')))
lastm = '0'
for i, m in enumerate(self.materials):
if (m == '0'):
self.materials[i] = lastm
else:
lastm = m
if node.hasAttribute('decoration'):
self.decoration = list(map(str,node.getAttribute('decoration').split(',')))
for childnode in node.childNodes:
if childnode.nodeName == 'Bone':
self.Bones.append(Bone(node=childnode))
class Brick:
def __init__(self, node):
self.refID = node.getAttribute('refID')
self.designID = node.getAttribute('designID')
self.Parts = []
for childnode in node.childNodes:
if childnode.nodeName == 'Part':
self.Parts.append(Part(node=childnode))
class SceneCamera:
def __init__(self, node):
self.refID = node.getAttribute('refID')
(a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(','))
self.matrix = Matrix3D(n11=a,n12=b,n13=c,n14=0,n21=d,n22=e,n23=f,n24=0,n31=g,n32=h,n33=i,n34=0,n41=x,n42=y,n43=z,n44=1)
self.fieldOfView = float(node.getAttribute('fieldOfView'))
self.distance = float(node.getAttribute('distance'))
class Scene:
def __init__(self, file):
self.Bricks = []
self.Scenecamera = []
self.Groups = []
if file.endswith('.lxfml'):
with open(file, "rb") as file:
data = file.read()
elif file.endswith('.lxf'):
zf = zipfile.ZipFile(file, 'r')
data = zf.read('IMAGE100.LXFML')
else:
return
xml = minidom.parseString(data)
self.Name = xml.firstChild.getAttribute('name')
for node in xml.firstChild.childNodes:
if node.nodeName == 'Meta':
for childnode in node.childNodes:
if childnode.nodeName == 'BrickSet':
self.Version = str(childnode.getAttribute('version'))
elif node.nodeName == 'Cameras':
for childnode in node.childNodes:
if childnode.nodeName == 'Camera':
self.Scenecamera.append(SceneCamera(node=childnode))
elif node.nodeName == 'Bricks':
for childnode in node.childNodes:
if childnode.nodeName == 'Brick':
self.Bricks.append(Brick(node=childnode))
elif node.nodeName == 'GroupSystems':
for childnode in node.childNodes:
if childnode.nodeName == 'GroupSystem':
for childnode in childnode.childNodes:
if childnode.nodeName == 'Group':
self.Groups.append(Group(node=childnode))
for i in range(len(self.Groups)):
for brick in self.Bricks:
for part in brick.Parts:
if part.refID in self.Groups[i].partRefs:
part.isGrouped = True
part.GroupIDX = i
print('Scene "'+ self.Name + '" Brickversion: ' + str(self.Version))
class GeometryReader:
def __init__(self, data):
self.offset = 0
self.data = data
self.positions = []
self.normals = []
self.textures = []
self.faces = []
self.bonemap = {}
self.texCount = 0
self.outpositions = []
self.outnormals = []
if self.readInt() == 1111961649:
self.valueCount = self.readInt()
self.indexCount = self.readInt()
self.faceCount = int(self.indexCount / 3)
options = self.readInt()
for i in range(0, self.valueCount):
self.positions.append(Point3D(x=self.readFloat(),y= self.readFloat(),z=self.readFloat()))
for i in range(0, self.valueCount):
self.normals.append(Point3D(x=self.readFloat(),y= self.readFloat(),z=self.readFloat()))
if (options & 3) == 3:
self.texCount = self.valueCount
for i in range(0, self.valueCount):
self.textures.append(Point2D(x=self.readFloat(), y=self.readFloat()))
for i in range(0, self.faceCount):
self.faces.append(Face(a=self.readInt(),b=self.readInt(),c=self.readInt()))
if (options & 48) == 48:
num = self.readInt()
self.offset += (num * 4) + (self.indexCount * 4)
num = self.readInt()
self.offset += (3 * num * 4) + (self.indexCount * 4)
bonelength = self.readInt()
self.bonemap = [0] * self.valueCount
if (bonelength > self.valueCount) or (bonelength > self.faceCount):
datastart = self.offset
self.offset += bonelength
for i in range(0, self.valueCount):
boneoffset = self.readInt() + 4
self.bonemap[i] = self.read_Int(datastart + boneoffset)
def read_Int(self,_offset):
if sys.version_info < (3, 0):
return int(struct.unpack_from('i', self.data, _offset)[0])
else:
return int.from_bytes(self.data[_offset:_offset + 4], byteorder='little')
def readInt(self):
if sys.version_info < (3, 0):
ret = int(struct.unpack_from('i', self.data, self.offset)[0])
else:
ret = int.from_bytes(self.data[self.offset:self.offset + 4], byteorder='little')
self.offset += 4
return ret
def readFloat(self):
ret = float(struct.unpack_from('f', self.data, self.offset)[0])
self.offset += 4
return ret
class Geometry:
def __init__(self, designID, database):
self.designID = designID
self.Parts = {}
GeometryLocation = '{0}{1}{2}'.format(GEOMETRIEPATH, designID,'.g')
GeometryCount = 0
while str(GeometryLocation) in database.filelist:
self.Parts[GeometryCount] = GeometryReader(data=database.filelist[GeometryLocation].read())
GeometryCount += 1
GeometryLocation = '{0}{1}{2}{3}'.format(GEOMETRIEPATH, designID,'.g',GeometryCount)
primitive = Primitive(data = database.filelist[PRIMITIVEPATH + designID + '.xml'].read())
self.Partname = primitive.Designname
# preflex
for part in self.Parts:
# transform
for i, b in enumerate(primitive.Bones):
# positions
for j, p in enumerate(self.Parts[part].positions):
if (self.Parts[part].bonemap[j] == i):
self.Parts[part].positions[j].transform(b.matrix)
# normals
for k, n in enumerate(self.Parts[part].normals):
if (self.Parts[part].bonemap[k] == i):
self.Parts[part].normals[k].transformW(b.matrix)
def valuecount(self):
count = 0
for part in self.Parts:
count += self.Parts[part].valueCount
return count
def facecount(self):
count = 0
for part in self.Parts:
count += self.Parts[part].faceCount
return count
def texcount(self):
count = 0
for part in self.Parts:
count += self.Parts[part].texCount
return count
class Bone2:
def __init__(self,boneId=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0):
self.boneId = boneId
rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0,axis = Point3D(x=ax,y=ay,z=az))
p = Point3D(x=tx,y=ty,z=tz)
p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x
rotationMatrix.n42 -= p.y
rotationMatrix.n43 -= p.z
self.matrix = rotationMatrix
class Primitive:
def __init__(self,data):
self.Designname = ''
self.Bones = []
xml = minidom.parseString(data)
for node in xml.firstChild.childNodes:
if node.nodeName == 'Flex':
for node in node.childNodes:
if node.nodeName == 'Bone':
self.Bones.append(Bone2(boneId=int(node.getAttribute('boneId')), angle=float(node.getAttribute('angle')), ax=float(node.getAttribute('ax')), ay=float(node.getAttribute('ay')), az=float(node.getAttribute('az')), tx=float(node.getAttribute('tx')), ty=float(node.getAttribute('ty')), tz=float(node.getAttribute('tz'))))
elif node.nodeName == 'Annotations':
for childnode in node.childNodes:
if childnode.nodeName == 'Annotation' and childnode.hasAttribute('designname'):
self.Designname = childnode.getAttribute('designname')
class LOCReader:
def __init__(self, data):
self.offset = 0
self.values = {}
self.data = data
if sys.version_info < (3, 0):
if ord(self.data[0]) == 50 and ord(self.data[1]) == 0:
self.offset += 2
while self.offset < len(self.data):
key = self.NextString().replace('Material', '')
value = self.NextString()
self.values[key] = value
else:
if int(self.data[0]) == 50 and int(self.data[1]) == 0:
self.offset += 2
while self.offset < len(self.data):
key = self.NextString().replace('Material', '')
value = self.NextString()
self.values[key] = value
def NextString(self):
out = ''
if sys.version_info < (3, 0):
t = ord(self.data[self.offset])
self.offset += 1
while not t == 0:
out = '{0}{1}'.format(out,chr(t))
t = ord(self.data[self.offset])
self.offset += 1
else:
t = int(self.data[self.offset])
self.offset += 1
while not t == 0:
out = '{0}{1}'.format(out,chr(t))
t = int(self.data[self.offset])
self.offset += 1
return out
class Materials:
def __init__(self, data):
self.Materials = {}
xml = minidom.parseString(data)
for node in xml.firstChild.childNodes:
if node.nodeName == 'Material':
self.Materials[node.getAttribute('MatID')] = Material(node.getAttribute('MatID'),r=int(node.getAttribute('Red')), g=int(node.getAttribute('Green')), b=int(node.getAttribute('Blue')), a=int(node.getAttribute('Alpha')), mtype=str(node.getAttribute('MaterialType')))
def setLOC(self, loc):
for key in loc.values:
if key in self.Materials:
self.Materials[key].name = loc.values[key].replace(" ", "_")
def getMaterialbyId(self, mid):
return self.Materials[mid]
class Material:
def __init__(self,id, r, g, b, a, mtype):
self.id = id
self.name = ''
self.mattype = mtype
self.r = float(r)
self.g = float(g)
self.b = float(b)
self.a = float(a)
def string(self):
out = 'Kd {0} {1} {2}\nKa 1.600000 1.600000 1.600000\nKs 0.400000 0.400000 0.400000\nNs 3.482202\nTf 1 1 1\n'.format( self.r / 255, self.g / 255,self.b / 255)
if self.a < 255:
out += 'Ni 1.575\n' + 'd {0}'.format(0.05) + '\n' + 'Tr {0}\n'.format(0.05)
return out
class DBinfo:
def __init__(self, data):
xml = minidom.parseString(data)
self.Version = xml.getElementsByTagName('Bricks')[0].attributes['version'].value
print('DB Version: ' + str(self.Version))
class LIFFile:
def __init__(self, name, offset, size, handle):
self.handle = handle
self.name = name
self.offset = offset
self.size = size
def read(self):
self.handle.seek(self.offset, 0)
return self.handle.read(self.size)
class LIFReader:
def __init__(self, file):
self.packedFilesOffset = 84
self.filelist = {}
self.initok = False
self.location = file
self.dbinfo = None
try:
self.filehandle = open(self.location, "rb")
self.filehandle.seek(0, 0)
except Exception as e:
self.initok = False
print("Database FAIL")
return
else:
if self.filehandle.read(4).decode() == "LIFF":
self.parse(prefix='', offset=self.readInt(offset=72) + 64)
if self.fileexist('/Materials.xml') and self.fileexist('/info.xml'):
self.dbinfo = DBinfo(data=self.filelist['/info.xml'].read())
print("Database OK.")
self.initok = True
else:
print("Database ERROR")
else:
print("Database FAIL")
self.initok = False
def fileexist(self,filename):
return filename in self.filelist
def parse(self, prefix='', offset=0):
if prefix == '':
offset += 36
else:
offset += 4
count = self.readInt(offset=offset)
for i in range(0, count):
offset += 4
entryType = self.readShort(offset=offset)
offset += 6
entryName = '{0}{1}'.format(prefix,'/');
self.filehandle.seek(offset + 1, 0)
if sys.version_info < (3, 0):
t = ord(self.filehandle.read(1))
else:
t = int.from_bytes(self.filehandle.read(1), byteorder='big')
while not t == 0:
entryName ='{0}{1}'.format(entryName,chr(t))
self.filehandle.seek(1, 1)
if sys.version_info < (3, 0):
t = ord(self.filehandle.read(1))
else:
t = int.from_bytes(self.filehandle.read(1), byteorder='big')
offset += 2
offset += 6
self.packedFilesOffset += 20
if entryType == 1:
offset = self.parse(prefix=entryName, offset=offset)
elif entryType == 2:
fileSize = self.readInt(offset=offset) - 20
self.filelist[entryName] = LIFFile(name=entryName, offset=self.packedFilesOffset, size=fileSize, handle=self.filehandle)
offset += 24
self.packedFilesOffset += fileSize
return offset
def readInt(self, offset=0):
self.filehandle.seek(offset, 0)
if sys.version_info < (3, 0):
return int(struct.unpack('>i', self.filehandle.read(4))[0])
else:
return int.from_bytes(self.filehandle.read(4), byteorder='big')
def readShort(self, offset=0):
self.filehandle.seek(offset, 0)
if sys.version_info < (3, 0):
return int(struct.unpack('>h', self.filehandle.read(2))[0])
else:
return int.from_bytes(self.filehandle.read(2), byteorder='big')
class Converter:
def LoadDatabase(self,databaselocation):
self.database = LIFReader(file=databaselocation)
if self.database.initok and self.database.fileexist('/Materials.xml') and self.database.fileexist(MATERIALNAMESPATH + 'EN/localizedStrings.loc'):
self.allMaterials = Materials(data=self.database.filelist['/Materials.xml'].read());
self.allMaterials.setLOC(loc=LOCReader(data=self.database.filelist[MATERIALNAMESPATH + 'EN/localizedStrings.loc'].read()))
def LoadScene(self,filename):
if self.database.initok:
self.scene = Scene(file=filename)
def Export(self,filename):
invert = Matrix3D()
#invert.n33 = -1 #uncomment to invert the Z-Axis
indexOffset = 1
textOffset = 1
usedmaterials = []
geometriecache = {}
start_time = time.time()
out = open(filename + ".obj", "w+")
out.write("mtllib " + filename + ".mtl" + '\n\n')
outtext = open(filename + ".mtl", "w+")
total = len(self.scene.Bricks)
current = 0
for bri in self.scene.Bricks:
current += 1
for pa in bri.Parts:
if pa.designID not in geometriecache:
geo = Geometry(designID=pa.designID, database=self.database)
progress(current ,total , "(" + geo.designID + ") " + geo.Partname, ' ')
geometriecache[pa.designID] = geo
else:
geo = geometriecache[pa.designID]
progress(current ,total , "(" + geo.designID + ") " + geo.Partname ,'-')
out.write("o\n")
for part in geo.Parts:
geo.Parts[part].outpositions = [elem.copy() for elem in geo.Parts[part].positions]
geo.Parts[part].outnormals = [elem.copy() for elem in geo.Parts[part].normals]
for i, b in enumerate(pa.Bones):
# positions
for j, p in enumerate(geo.Parts[part].outpositions):
if (geo.Parts[part].bonemap[j] == i):
p.transform( invert * b.matrix)
# normals
for k, n in enumerate(geo.Parts[part].outnormals):
if (geo.Parts[part].bonemap[k] == i):
n.transformW( invert * b.matrix)
for point in geo.Parts[part].outpositions:
out.write(point.string("v"))
for normal in geo.Parts[part].outnormals:
out.write(normal.string("vn"))
for text in geo.Parts[part].textures:
out.write(text.string("vt"))
decoCount = 0
out.write("g " + "(" + geo.designID + ") " + geo.Partname + '\n')
for part in geo.Parts:
lddmat = self.allMaterials.getMaterialbyId(pa.materials[part])
matname = lddmat.name
deco = '0'
if hasattr(pa, 'decoration') and len(geo.Parts[part].textures) > 0:
if decoCount <= len(pa.decoration):
deco = pa.decoration[decoCount]
decoCount += 1
extfile = ''
if not deco == '0':
extfile = deco + '.png'
matname += "_" + deco
decofilename = DECORATIONPATH + deco + '.png'
if not os.path.isfile(extfile) and self.database.fileexist(decofilename):
with open(extfile, "wb") as f:
f.write(self.database.filelist[decofilename].read())
f.close()
if not matname in usedmaterials:
usedmaterials.append(matname)
outtext.write("newmtl " + matname + '\n')
outtext.write(lddmat.string())
if not deco == '0':
outtext.write("map_Kd " + deco + ".png" + '\n')
out.write("usemtl " + matname + '\n')
for face in geo.Parts[part].faces:
if len(geo.Parts[part].textures) > 0:
out.write(face.string("f",indexOffset,textOffset))
else:
out.write(face.string("f",indexOffset))
indexOffset += len(geo.Parts[part].outpositions)
textOffset += len(geo.Parts[part].textures)
# -----------------------------------------------------------------
out.write('\n')
sys.stdout.write('%s\r' % (' '))
print("--- %s seconds ---" % (time.time() - start_time))
def FindDatabase():
if os.name =='posix':
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db.lif'))
else:
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db.lif'))
def progress(count, total, status='', suffix = ''):
bar_len = 40
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '#' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, ' '))
sys.stdout.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, status))
sys.stdout.flush()
def main():
print("- - - pyldd2obj - - -")
print(" _ ")
print(" [_]")
print(" /| |\\")
print(" ()'---' C")
print(" | | |")
print(" [=|=]")
print("")
print("- - - - - - - - - - - -")
try:
lxf_filename = sys.argv[1]
obj_filename = sys.argv[2]
except Exception as e:
print("Missing Paramenter:" + sys.argv[0] + " infile.lfx exportname (without extension)")
return
converter = Converter()
if os.path.exists(FindDatabase()):
converter.LoadDatabase(databaselocation = FindDatabase())
converter.LoadScene(filename=lxf_filename)
converter.Export(filename=obj_filename)
else:
print("no LDD database found please install LEGO-Digital-Designer")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment