Init Repo
This commit is contained in:
commit
4c8bf1460f
|
|
@ -0,0 +1,143 @@
|
||||||
|
import maya.OpenMaya as OpenMaya
|
||||||
|
import maya.OpenMayaMPx as OpenMayaMPx
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import subprocess as sp
|
||||||
|
import reader.lxf_loader as lxf_loader
|
||||||
|
import reader.ldd_command as ldd_command
|
||||||
|
|
||||||
|
reload(lxf_loader)
|
||||||
|
reload(ldd_command)
|
||||||
|
|
||||||
|
logger = logging.getLogger('LDD')
|
||||||
|
|
||||||
|
kFileVersion = ''
|
||||||
|
kExtension = '.lxf'
|
||||||
|
kPluginName = 'Lego Digital Designer Reader for Maya '
|
||||||
|
kFilterName = '*.lxf'
|
||||||
|
kTranslatorName = 'LXF File'
|
||||||
|
|
||||||
|
|
||||||
|
class LXFReader(OpenMayaMPx.MPxFileTranslator):
|
||||||
|
def __init__(self):
|
||||||
|
OpenMayaMPx.MPxFileTranslator.__init__(self)
|
||||||
|
|
||||||
|
def haveReadMethod(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def haveWriteMethod(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def canBeOpened(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def translatorName(self):
|
||||||
|
return kTranslatorName
|
||||||
|
|
||||||
|
def defaultExtension(self):
|
||||||
|
return kExtension
|
||||||
|
|
||||||
|
def filter(self):
|
||||||
|
return kFilterName
|
||||||
|
|
||||||
|
def identifyFile(self, fileName, buf, size):
|
||||||
|
if 'lxf' in fileName.name().lower():
|
||||||
|
logger.info('File is LDD lxf %s' % fileName.name())
|
||||||
|
return OpenMayaMPx.MPxFileTranslator.kIsMyFileType
|
||||||
|
|
||||||
|
return OpenMayaMPx.MPxFileTranslator.kNotMyFileType
|
||||||
|
|
||||||
|
def reader(self, fileName, options, mode):
|
||||||
|
logger.info('reader')
|
||||||
|
try:
|
||||||
|
loader = lxf_loader.LXFLoader()
|
||||||
|
loader.read(fileName.rawFullName())
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Failed reader : %s" % kPluginName)
|
||||||
|
(typ, value, traceback) = sys.exc_info()
|
||||||
|
sys.excepthook(typ, value, traceback)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def lxf_reader_creator():
|
||||||
|
return OpenMayaMPx.asMPxPtr(LXFReader())
|
||||||
|
|
||||||
|
|
||||||
|
def check_ldd_install():
|
||||||
|
program_dir = os.getenv('ProgramFiles(x86)')
|
||||||
|
ldd_install_dir = os.path.join(program_dir, 'LEGO Company', 'LEGO Digital Designer')
|
||||||
|
exec_file = os.path.normpath(os.path.join(ldd_install_dir, 'LDD.exe'))
|
||||||
|
|
||||||
|
if not os.path.exists(exec_file):
|
||||||
|
return False
|
||||||
|
|
||||||
|
cmd = r'powershell.exe [System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"%s\").FileVersion' % exec_file
|
||||||
|
ldd_version = ''
|
||||||
|
p = sp.Popen(cmd, shell=True, stdout=sp.PIPE)
|
||||||
|
|
||||||
|
for li in p.stdout:
|
||||||
|
ldd_version = li.strip()
|
||||||
|
|
||||||
|
asset_file = os.path.join(ldd_install_dir, 'Assets.lif')
|
||||||
|
|
||||||
|
if not os.path.exists(asset_file):
|
||||||
|
logger.warning('LEGO Digital Designer Assets.lif not found.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug('LEGO Digital Designer %s installed.' % ldd_version)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def init_ldd_asset_map():
|
||||||
|
""" Pre generate asset.lif data index
|
||||||
|
for reading geometry data
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def initializePlugin(mobject):
|
||||||
|
if os.getenv('OS') not in ['Windows_NT']:
|
||||||
|
sys.stderr.write('LDD.py only support window platform.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if not check_ldd_install():
|
||||||
|
sys.stderr.write('LEGO Digital Designer not install.')
|
||||||
|
return
|
||||||
|
|
||||||
|
mplugin = OpenMayaMPx.MFnPlugin(mobject, "CGCG", "1.0", "Any")
|
||||||
|
try:
|
||||||
|
mplugin.registerFileTranslator(kTranslatorName,
|
||||||
|
None,
|
||||||
|
lxf_reader_creator,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
False)
|
||||||
|
logger.debug('Register LDD File Translater :: %s' % kTranslatorName)
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Failed to register translator: %s\n" % kPluginName)
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
mplugin.registerCommand(ldd_command.kPluginCmdName,
|
||||||
|
ldd_command.ldd_command_creater,
|
||||||
|
ldd_command.ldd_command_syntaxCreator)
|
||||||
|
logger.debug('Register LDD Command :: %s' % ldd_command.kPluginCmdName)
|
||||||
|
except:
|
||||||
|
sys.stderr.write('Failed to register command: ' + ldd_command.kPluginCmdName)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def uninitializePlugin(mobject):
|
||||||
|
mplugin = OpenMayaMPx.MFnPlugin(mobject)
|
||||||
|
try:
|
||||||
|
mplugin.deregisterFileTranslator(kTranslatorName)
|
||||||
|
logger.debug('Deregister LDD File Translator :: %s' % kTranslatorName)
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Failed to deregister translator: %s\n" % kPluginName)
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
mplugin.deregisterCommand(ldd_command.kPluginCmdName)
|
||||||
|
except:
|
||||||
|
sys.stderr.write('Failed to deregister command: ' + ldd_command.kPluginCmdName)
|
||||||
|
raise
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Lego Digital Designer Reader for Maya
|
||||||
|
This plugin is for tranlate geometry from Lego Digital Designer, extract grometry from lego part number and material color. The base idea is from git project ***[LIF Extractor](https://github.com/JrMasterModelBuilder/LIF-Extractor)***
|
||||||
|
|
||||||
|
|
||||||
|
### Requirement
|
||||||
|
* Lego Digital Designer 4.3+
|
||||||
|
* Maya 2018 +
|
||||||
|
|
||||||
|
### Limitation
|
||||||
|
Current some geometry with hole will translate incorrect result and uvs.
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,169 @@
|
||||||
|
"""
|
||||||
|
LIF Extractor - LEGO Digital Designer LIF extractor.
|
||||||
|
|
||||||
|
Copyright (C) 2012 JrMasterModelBuilder
|
||||||
|
|
||||||
|
You accept full responsibility for how you use this program.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
LDD_INSTALL_PATH = r'C:\Program Files (x86)\LEGO Company\LEGO Digital Designer'
|
||||||
|
LDD_ASSET_PATH = os.path.join(LDD_INSTALL_PATH, 'Assets.lif')
|
||||||
|
|
||||||
|
fileData = ''
|
||||||
|
|
||||||
|
|
||||||
|
def asset_extract(design_ids=None, material_only=False, debug=True):
|
||||||
|
global fileData
|
||||||
|
|
||||||
|
def uint32(offset):
|
||||||
|
if bytesAre == "str":
|
||||||
|
return (ord(fileData[offset]) * 16777216) + (ord(fileData[offset + 1]) * 65536) + (
|
||||||
|
ord(fileData[offset + 2]) * 256) + ord(fileData[offset + 3])
|
||||||
|
else:
|
||||||
|
return (fileData[offset] * 16777216) + (fileData[offset + 1] * 65536) + (fileData[offset + 2] * 256) + \
|
||||||
|
fileData[offset + 3]
|
||||||
|
|
||||||
|
def uint16(offset):
|
||||||
|
if bytesAre == "str":
|
||||||
|
return (ord(fileData[offset]) * 256) + ord(fileData[offset + 1])
|
||||||
|
else:
|
||||||
|
return (fileData[offset] * 256) + fileData[offset + 1]
|
||||||
|
|
||||||
|
def recurse(prefix, offset):
|
||||||
|
if prefix == "":
|
||||||
|
offset += 36
|
||||||
|
else:
|
||||||
|
folderList.extend([prefix])
|
||||||
|
offset += 4
|
||||||
|
for i in range(uint32(offset)):
|
||||||
|
offset += 4
|
||||||
|
entryType = uint16(offset) # 1 = directory, 2 = file
|
||||||
|
print 'xxx', entryType, offset
|
||||||
|
offset += 6
|
||||||
|
# Build the name.
|
||||||
|
entryName = os.sep
|
||||||
|
# Check both integer and byte for different versions of Python.
|
||||||
|
while fileData[offset + 1] != 0 and fileData[offset + 1] != b"\x00":
|
||||||
|
print '---', offset, fileData[offset + 1]
|
||||||
|
if bytesAre == "str":
|
||||||
|
entryName += fileData[offset + 1]
|
||||||
|
else:
|
||||||
|
entryName += chr(fileData[offset + 1])
|
||||||
|
offset += 2
|
||||||
|
offset += 6
|
||||||
|
|
||||||
|
if entryType == 1:
|
||||||
|
# Recurse through the director, and update the offset.
|
||||||
|
packedFilesOffset[0] += 20
|
||||||
|
offset = recurse(prefix + entryName, offset)
|
||||||
|
elif entryType == 2:
|
||||||
|
# File offset.
|
||||||
|
packedFilesOffset[0] += 20
|
||||||
|
fileOffset = packedFilesOffset[0]
|
||||||
|
# File size.
|
||||||
|
fileSize = uint32(offset) - 20
|
||||||
|
offset += 24
|
||||||
|
packedFilesOffset[0] += fileSize
|
||||||
|
fileList.extend([[prefix + entryName, fileOffset, fileSize]])
|
||||||
|
# Return the offset at the end of this run to update parent runnings.
|
||||||
|
return offset
|
||||||
|
if debug:
|
||||||
|
logger.debug('PROCESSING: %s' % LDD_ASSET_PATH)
|
||||||
|
# Open the file if valid.
|
||||||
|
try:
|
||||||
|
with open(LDD_ASSET_PATH, "rb") as f:
|
||||||
|
fileData = f.read()
|
||||||
|
|
||||||
|
except IOError as e:
|
||||||
|
logger.error('ERROR: Failed to read file.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(fileData) < 4 or fileData[0:4] != b"LIFF":
|
||||||
|
logger.error('ERROR: Not a LIF file.')
|
||||||
|
return
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
logger.debug('EXTRACTING: Asset.lif: Please wait.')
|
||||||
|
|
||||||
|
# Recurse through, creating a list of all the files.
|
||||||
|
packedFilesOffset = [84] # Array for non-global function-modifiable variable.
|
||||||
|
folderList = []
|
||||||
|
fileList = []
|
||||||
|
|
||||||
|
# Check if this version of Python treats bytes as int or str
|
||||||
|
bytesAre = type(b'a'[0]).__name__
|
||||||
|
|
||||||
|
# Recuse through the archive.
|
||||||
|
recurse("", (uint32(72) + 64))
|
||||||
|
|
||||||
|
for i in fileList:
|
||||||
|
if 'db.lif' in i[0]:
|
||||||
|
fileData = fileData[i[1]:i[1] + i[2]]
|
||||||
|
break
|
||||||
|
if debug:
|
||||||
|
logger.debug('PROCESSING: db.lif')
|
||||||
|
if len(fileData) < 4 or fileData[0:4] != b"LIFF":
|
||||||
|
logger.error("\tERROR: Not a LIF file.")
|
||||||
|
return
|
||||||
|
if debug:
|
||||||
|
logger.debug('EXTRACTING db.lif: Please wait.')
|
||||||
|
# Recurse through, creating a list of all the files.
|
||||||
|
packedFilesOffset = [84] # Array for non-global function-modifiable variable.
|
||||||
|
folderList = []
|
||||||
|
fileList = []
|
||||||
|
|
||||||
|
# Check if this version of Python treats bytes as int or str
|
||||||
|
bytesAre = type(b'a'[0]).__name__
|
||||||
|
|
||||||
|
# Recuse through the archive.
|
||||||
|
recurse("", (uint32(72) + 64))
|
||||||
|
|
||||||
|
if material_only:
|
||||||
|
for i in fileList:
|
||||||
|
material_file = r'\Materials.xml'
|
||||||
|
if i[0] in [material_file]:
|
||||||
|
m_data = fileData[i[1]:i[1] + i[2]]
|
||||||
|
return m_data
|
||||||
|
return None
|
||||||
|
|
||||||
|
if design_ids:
|
||||||
|
g_data_map = {}
|
||||||
|
g_file_list = [r'\Primitives\LOD0\%s.g' % d for d in design_ids]
|
||||||
|
for i in fileList:
|
||||||
|
if i[0] not in g_file_list:
|
||||||
|
continue
|
||||||
|
g_data = fileData[i[1]:i[1] + i[2]]
|
||||||
|
design_id = design_ids[g_file_list.index(i[0])]
|
||||||
|
g_data_map[design_id] = g_data
|
||||||
|
|
||||||
|
return g_data_map
|
||||||
|
else:
|
||||||
|
design_id_list = []
|
||||||
|
for i in fileList:
|
||||||
|
if r'\Primitives\LOD0' not in i[0]:
|
||||||
|
continue
|
||||||
|
design_id = os.path.splitext(os.path.basename(i[0]))[0]
|
||||||
|
design_id_list.append(design_id)
|
||||||
|
|
||||||
|
return design_id_list
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asset_extract('99818', material_only=True)
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
import asset_reader
|
||||||
|
from lxf_loader import LXFLoader
|
||||||
|
import maya.api.OpenMaya as OpenMaya
|
||||||
|
import maya.OpenMaya as om
|
||||||
|
import maya.OpenMayaMPx as OpenMayaMPx
|
||||||
|
import logging
|
||||||
|
import maya.cmds as mc
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
def load_part(design_ids, debug=False):
|
||||||
|
# design_ids = ['99251']
|
||||||
|
part_data_map = asset_reader.asset_extract(design_ids=design_ids, debug=debug)
|
||||||
|
|
||||||
|
mesh_created = []
|
||||||
|
if not part_data_map:
|
||||||
|
return mesh_created
|
||||||
|
|
||||||
|
for design_id in design_ids:
|
||||||
|
if debug:
|
||||||
|
logger.debug('Load LDD part %s' % design_id)
|
||||||
|
status, v_list, n_list, f_ids = LXFLoader.get_geometry(design_id, part_data_map, debug=debug)
|
||||||
|
vertices = [OpenMaya.MPoint(v_list[x], v_list[x + 1], v_list[x + 2]) for x in
|
||||||
|
range(0, len(v_list), 3)]
|
||||||
|
|
||||||
|
polygon_counts = [3 for i in range(0, len(f_ids), 3)]
|
||||||
|
fn_mesh = OpenMaya.MFnMesh()
|
||||||
|
fn_mesh.create(vertices, polygon_counts, f_ids)
|
||||||
|
mesh_parent = fn_mesh.parent(0)
|
||||||
|
mesh_transform = OpenMaya.MFnTransform(mesh_parent)
|
||||||
|
mesh_transform.setName('lego_part_%s_mesh' % design_id)
|
||||||
|
force_element = 'initialShadingGroup'
|
||||||
|
mc.sets(mesh_transform.fullPathName(), e=True, forceElement=force_element)
|
||||||
|
if debug:
|
||||||
|
logger.debug('Load LDD part %s finished.' % design_id)
|
||||||
|
mesh_created.append(mesh_transform.fullPathName())
|
||||||
|
|
||||||
|
return mesh_created
|
||||||
|
|
||||||
|
|
||||||
|
def part_exists(design_id, debug=False):
|
||||||
|
design_id_list = asset_reader.asset_extract(debug=debug)
|
||||||
|
if design_id in design_id_list:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_part_list(debug=False):
|
||||||
|
return asset_reader.asset_extract(debug=debug)
|
||||||
|
|
||||||
|
|
||||||
|
kPluginCmdName = 'ldd'
|
||||||
|
kImportFlag = '-ip'
|
||||||
|
kImportFlagLong = '-importPart'
|
||||||
|
kExistsFlag = '-ex'
|
||||||
|
kExistsFlagLong = '-exists'
|
||||||
|
kListFlag = '-lp'
|
||||||
|
kListFlagLong = '-listParts'
|
||||||
|
kDebugFlag = '-d'
|
||||||
|
kDebugFlagLong = '-debug'
|
||||||
|
|
||||||
|
|
||||||
|
class LddCommand(OpenMayaMPx.MPxCommand):
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor. """
|
||||||
|
OpenMayaMPx.MPxCommand.__init__(self)
|
||||||
|
|
||||||
|
def doIt(self, args):
|
||||||
|
return self.parseArguments(args)
|
||||||
|
|
||||||
|
def parseArguments(self, args):
|
||||||
|
arg_data = om.MArgParser(self.syntax(), args)
|
||||||
|
|
||||||
|
debug = False
|
||||||
|
if arg_data.isFlagSet(kDebugFlagLong):
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
if arg_data.isFlagSet(kImportFlagLong):
|
||||||
|
design_id_str = arg_data.flagArgumentString(kImportFlagLong, 0)
|
||||||
|
design_ids = [design_id_str]
|
||||||
|
if ',' in design_id_str:
|
||||||
|
design_ids = design_id_str.split(',')
|
||||||
|
|
||||||
|
self.setResult(load_part(design_ids, debug=debug))
|
||||||
|
|
||||||
|
if arg_data.isFlagSet(kExistsFlagLong):
|
||||||
|
design_id = arg_data.flagArgumentString(kExistsFlagLong, 0)
|
||||||
|
self.setResult(part_exists(design_id, debug=debug))
|
||||||
|
|
||||||
|
if arg_data.isFlagSet(kListFlagLong):
|
||||||
|
self.setResult(sorted(get_part_list(debug=debug)))
|
||||||
|
|
||||||
|
|
||||||
|
def ldd_command_syntaxCreator():
|
||||||
|
syntax = om.MSyntax()
|
||||||
|
syntax.addFlag(kImportFlag, kImportFlagLong, om.MSyntax.kString)
|
||||||
|
syntax.addFlag(kExistsFlag, kExistsFlagLong, om.MSyntax.kString)
|
||||||
|
syntax.addFlag(kListFlag, kListFlagLong, om.MSyntax.kNoArg)
|
||||||
|
syntax.addFlag(kDebugFlag, kDebugFlagLong, om.MSyntax.kNoArg)
|
||||||
|
return syntax
|
||||||
|
|
||||||
|
|
||||||
|
def ldd_command_creater():
|
||||||
|
return OpenMayaMPx.asMPxPtr(LddCommand())
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
|
||||||
|
LDD_INSTALL_PATH = r'C:\Program Files (x86)\LEGO Company\LEGO Digital Designer'
|
||||||
|
LDD_ASSET_PATH = os.path.join(LDD_INSTALL_PATH, 'Assets.lif')
|
||||||
|
|
||||||
|
|
||||||
|
def read():
|
||||||
|
def uint32(fd, offset):
|
||||||
|
fd.seek(offset)
|
||||||
|
buff = fd.read(4)
|
||||||
|
if bytesAre == 'str':
|
||||||
|
return (ord(buff[0]) * 16777216) + \
|
||||||
|
(ord(buff[1]) * 65536) + \
|
||||||
|
(ord(buff[2]) * 256) + \
|
||||||
|
ord(buff[3])
|
||||||
|
else:
|
||||||
|
return (buff[0] * 16777216) + \
|
||||||
|
(buff[1] * 65536) + \
|
||||||
|
(buff[2] * 256) + \
|
||||||
|
buff[3]
|
||||||
|
|
||||||
|
def uint16(fd, offset):
|
||||||
|
fd.seek(offset)
|
||||||
|
buff = fd.read(2)
|
||||||
|
if bytesAre == "str":
|
||||||
|
return (ord(buff[0]) * 256) + \
|
||||||
|
ord(buff[1])
|
||||||
|
else:
|
||||||
|
return (fd.read(1) * 256) + \
|
||||||
|
fd.read(1)
|
||||||
|
|
||||||
|
def recurse(fd, prefix, offset):
|
||||||
|
if prefix == "":
|
||||||
|
offset += 36
|
||||||
|
else:
|
||||||
|
folder_list.extend([prefix])
|
||||||
|
offset += 4
|
||||||
|
for i in range(uint32(fd, offset)):
|
||||||
|
offset += 4
|
||||||
|
entryType = uint16(fd, offset) # 1 = directory, 2 = file
|
||||||
|
print 'xxx', entryType, offset
|
||||||
|
offset += 6
|
||||||
|
|
||||||
|
# Build the name.offset
|
||||||
|
entryName = os.sep
|
||||||
|
|
||||||
|
while True:
|
||||||
|
fd.seek(offset)
|
||||||
|
read_chr = ''.join(struct.unpack('<s', fd.read(1)))
|
||||||
|
print '---', offset, read_chr
|
||||||
|
if not (read_chr != 0 and read_chr != b'\x00'):
|
||||||
|
break
|
||||||
|
if bytesAre == 'str':
|
||||||
|
entryName += read_chr
|
||||||
|
else:
|
||||||
|
entryName += chr(read_chr)
|
||||||
|
offset += 2
|
||||||
|
|
||||||
|
offset += 6
|
||||||
|
|
||||||
|
if entryType == 1:
|
||||||
|
packed_files_offset[0] += 20
|
||||||
|
offset = recurse(fd, prefix + entryName, offset)
|
||||||
|
|
||||||
|
elif entryType == 2:
|
||||||
|
|
||||||
|
packed_files_offset[0] += 20
|
||||||
|
fileOffset = packed_files_offset[0]
|
||||||
|
# File size.
|
||||||
|
fileSize = uint32(fd, offset) - 20
|
||||||
|
offset += 24
|
||||||
|
packed_files_offset[0] += fileSize
|
||||||
|
file_list.extend([[prefix + entryName, fileOffset, fileSize]])
|
||||||
|
|
||||||
|
return offset
|
||||||
|
|
||||||
|
bytesAre = type(b'a'[0]).__name__
|
||||||
|
packed_files_offset = [84] # Array for non-global function-modifiable variable.
|
||||||
|
folder_list = []
|
||||||
|
file_list = []
|
||||||
|
with open(LDD_ASSET_PATH, "rb") as fd:
|
||||||
|
file_format = ''.join(struct.unpack('<ssss', fd.read(4)))
|
||||||
|
if file_format != 'LIFF':
|
||||||
|
return
|
||||||
|
print file_format
|
||||||
|
|
||||||
|
recurse(fd, "", (uint32(fd, 72) + 64))
|
||||||
|
|
||||||
|
for i in file_list:
|
||||||
|
print i
|
||||||
|
|
||||||
|
|
||||||
|
# read()
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
import struct
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import asset_reader
|
||||||
|
import cStringIO
|
||||||
|
import maya.api.OpenMaya as OpenMaya
|
||||||
|
import maya.cmds as mc
|
||||||
|
import maya.mel as mm
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
class LXFLoader(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.material_index = {}
|
||||||
|
|
||||||
|
def get_material(self):
|
||||||
|
self.material_index = {}
|
||||||
|
|
||||||
|
material_data = asset_reader.asset_extract(material_only=True)
|
||||||
|
if not material_data:
|
||||||
|
logger.warning('Can\'t extract material info')
|
||||||
|
return False
|
||||||
|
|
||||||
|
root = ET.fromstring(material_data)
|
||||||
|
|
||||||
|
gMainProgressBar = mm.eval('$tmp = $gMainProgressBar')
|
||||||
|
|
||||||
|
mc.progressBar(gMainProgressBar,
|
||||||
|
edit=True,
|
||||||
|
beginProgress=True,
|
||||||
|
isInterruptable=True,
|
||||||
|
status='Reading LDD materials ...',
|
||||||
|
maxValue=len(root))
|
||||||
|
|
||||||
|
logger.info('Get material data ...')
|
||||||
|
|
||||||
|
for materail in root:
|
||||||
|
if mc.progressBar(gMainProgressBar, query=True, isCancelled=True):
|
||||||
|
mc.progressBar(gMainProgressBar, edit=True, endProgress=True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
mat_id = materail.get('MatID')
|
||||||
|
mat_r = materail.get('Red')
|
||||||
|
mat_g = materail.get('Green')
|
||||||
|
mat_b = materail.get('Blue')
|
||||||
|
mat_a = materail.get('Alpha')
|
||||||
|
|
||||||
|
if mat_id not in self.material_index.keys():
|
||||||
|
self.material_index[mat_id] = {'r': int(mat_r),
|
||||||
|
'g': int(mat_g),
|
||||||
|
'b': int(mat_b),
|
||||||
|
'a': int(mat_a)}
|
||||||
|
|
||||||
|
mc.progressBar(gMainProgressBar, edit=True, step=1)
|
||||||
|
|
||||||
|
mc.progressBar(gMainProgressBar, edit=True, endProgress=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_lxfml_name(zipf):
|
||||||
|
name_list = zipf.namelist()
|
||||||
|
for name in name_list:
|
||||||
|
if os.path.splitext(name)[-1] in ['.LXFML']:
|
||||||
|
return name
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_geometry(design_id, part_data_map, output=None, debug=True):
|
||||||
|
|
||||||
|
def convert_to_obj(v_list, n_list, f_ids, output_file):
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
f.write("# OBJ file\n")
|
||||||
|
for vid in range(0, len(v_list), 3):
|
||||||
|
f.write('v %s %s %s\n' % (v_list[vid], v_list[vid + 1], v_list[vid + 2]))
|
||||||
|
f.write('\n')
|
||||||
|
|
||||||
|
for nid in range(0, len(n_list), 3):
|
||||||
|
f.write('vn %s %s %s\n' % (n_list[nid], n_list[nid + 1], n_list[nid + 2]))
|
||||||
|
|
||||||
|
f.write('\n')
|
||||||
|
for fid in range(0, len(f_ids), 3):
|
||||||
|
f.write('f %s %s %s\n' % (f_ids[fid] + 1, f_ids[fid + 1] + 1, f_ids[fid + 2] + 1))
|
||||||
|
|
||||||
|
if design_id not in part_data_map.keys():
|
||||||
|
return False, [], [], []
|
||||||
|
|
||||||
|
vertex = []
|
||||||
|
normal = []
|
||||||
|
index = []
|
||||||
|
|
||||||
|
g_buffer = part_data_map[design_id]
|
||||||
|
|
||||||
|
f = cStringIO.StringIO(g_buffer)
|
||||||
|
|
||||||
|
# Read header part
|
||||||
|
struct.unpack('<i', f.read(4))
|
||||||
|
vertex_count = struct.unpack('<i', f.read(4))[0]
|
||||||
|
index_count = struct.unpack('<i', f.read(4))[0]
|
||||||
|
struct.unpack('<i', f.read(4))
|
||||||
|
if debug:
|
||||||
|
logger.debug('-' * 60)
|
||||||
|
logger.debug('\tRead Part %s : vertex count : %s' % (design_id, vertex_count))
|
||||||
|
logger.debug('\tRead Part %s : index count : %s' % (design_id, index_count))
|
||||||
|
logger.debug('-' * 60)
|
||||||
|
|
||||||
|
# Read content part
|
||||||
|
for i in range(vertex_count):
|
||||||
|
vertex.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
vertex.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
vertex.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
|
||||||
|
for i in range(vertex_count):
|
||||||
|
normal.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
normal.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
normal.append(struct.unpack('<f', f.read(4))[0])
|
||||||
|
|
||||||
|
for i in range(index_count):
|
||||||
|
index.append(struct.unpack('<i', f.read(4))[0])
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if output:
|
||||||
|
convert_to_obj(vertex, normal, index, output)
|
||||||
|
return True, vertex, normal, index
|
||||||
|
|
||||||
|
return True, vertex, normal, index
|
||||||
|
|
||||||
|
def read(self, file_path_name):
|
||||||
|
has_material = self.get_material()
|
||||||
|
|
||||||
|
part_cache = {}
|
||||||
|
zipf = ZipFile(file_path_name)
|
||||||
|
lxfml = self.get_lxfml_name(zipf)
|
||||||
|
lxfml_content = zipf.open(lxfml).read()
|
||||||
|
root = ET.fromstring(lxfml_content)
|
||||||
|
part_data_list = []
|
||||||
|
design_ids = []
|
||||||
|
|
||||||
|
for element in root:
|
||||||
|
if element.tag == 'Bricks':
|
||||||
|
for brick in element:
|
||||||
|
for part in brick:
|
||||||
|
design_id = part.get('designID')
|
||||||
|
mat_id = part.get('materials')
|
||||||
|
|
||||||
|
for bone in part:
|
||||||
|
m = [float(x) for x in bone.get('transformation').split(',')]
|
||||||
|
part_data = dict(designID=design_id, materials=mat_id, transformation=m)
|
||||||
|
part_data_list.append(part_data)
|
||||||
|
if design_id not in design_ids:
|
||||||
|
design_ids.append(design_id)
|
||||||
|
|
||||||
|
part_data_map = asset_reader.asset_extract(design_ids=design_ids)
|
||||||
|
|
||||||
|
if not part_data_map:
|
||||||
|
return
|
||||||
|
|
||||||
|
gMainProgressBar = mm.eval('$tmp = $gMainProgressBar')
|
||||||
|
|
||||||
|
mc.progressBar(gMainProgressBar,
|
||||||
|
edit=True,
|
||||||
|
beginProgress=True,
|
||||||
|
isInterruptable=True,
|
||||||
|
status='Importing LDD models ...',
|
||||||
|
maxValue=len(part_data_list))
|
||||||
|
|
||||||
|
for part_data in part_data_list:
|
||||||
|
|
||||||
|
if mc.progressBar(gMainProgressBar, query=True, isCancelled=True):
|
||||||
|
logger.warning('Import LDD models interrupt...')
|
||||||
|
break
|
||||||
|
|
||||||
|
design_id = part_data['designID']
|
||||||
|
mat_id = part_data['materials']
|
||||||
|
m = part_data['transformation']
|
||||||
|
|
||||||
|
if design_id not in part_cache.keys():
|
||||||
|
logger.debug(
|
||||||
|
'[%s/%s] Get new part %s' % (part_data_list.index(part_data), len(part_data_list), design_id))
|
||||||
|
status, v_list, n_list, f_ids = self.get_geometry(design_id, part_data_map)
|
||||||
|
part_cache[design_id] = [v_list, f_ids]
|
||||||
|
else:
|
||||||
|
logger.debug(
|
||||||
|
'[%s/%s] Get cached part of %s' % (part_data_list.index(part_data), len(part_data_list), design_id))
|
||||||
|
v_list = part_cache[design_id][0]
|
||||||
|
f_ids = part_cache[design_id][1]
|
||||||
|
|
||||||
|
vertices = [OpenMaya.MPoint(v_list[x], v_list[x + 1], v_list[x + 2]) for x in
|
||||||
|
range(0, len(v_list), 3)]
|
||||||
|
polygon_counts = [3 for i in range(0, len(f_ids), 3)]
|
||||||
|
fn_mesh = OpenMaya.MFnMesh()
|
||||||
|
fn_mesh.create(vertices, polygon_counts, f_ids)
|
||||||
|
mesh_parent = fn_mesh.parent(0)
|
||||||
|
mesh_transform = OpenMaya.MFnTransform(mesh_parent)
|
||||||
|
mesh_transform.setName('lego_part_%s_mesh' % design_id)
|
||||||
|
m_matrix = OpenMaya.MMatrix([m[0], m[1], m[2], 0,
|
||||||
|
m[3], m[4], m[5], 0,
|
||||||
|
m[6], m[7], m[8], 0,
|
||||||
|
m[9], m[10], m[11], 1])
|
||||||
|
transform_matrix = OpenMaya.MTransformationMatrix(m_matrix)
|
||||||
|
mesh_transform.setTransformation(transform_matrix)
|
||||||
|
|
||||||
|
force_element = 'initialShadingGroup'
|
||||||
|
|
||||||
|
if has_material:
|
||||||
|
if mat_id in self.material_index.keys():
|
||||||
|
shd_name = 'shd_%s' % mat_id
|
||||||
|
sg_name = '%sSG' % shd_name
|
||||||
|
if not mc.objExists(shd_name):
|
||||||
|
color_data = self.material_index[mat_id]
|
||||||
|
r = float(color_data.get('r')) / 255.0
|
||||||
|
g = float(color_data.get('g')) / 255.0
|
||||||
|
b = float(color_data.get('b')) / 255.0
|
||||||
|
a = float(color_data.get('a')) / 255.0
|
||||||
|
transparency = 1.0 - a
|
||||||
|
mc.shadingNode('blinn', asShader=True, name=shd_name)
|
||||||
|
mc.setAttr('%s.color' % shd_name, r, g, b, type='double3')
|
||||||
|
mc.setAttr('%s.transparency' % shd_name,
|
||||||
|
transparency, transparency, transparency,
|
||||||
|
type='double3')
|
||||||
|
mc.sets(renderable=True, noSurfaceShader=True, empty=True, name=sg_name)
|
||||||
|
mc.connectAttr('%s.outColor' % shd_name, '%s.surfaceShader' % sg_name, f=True)
|
||||||
|
|
||||||
|
force_element = sg_name
|
||||||
|
|
||||||
|
mc.sets(mesh_transform.fullPathName(), e=True, forceElement=force_element)
|
||||||
|
mc.progressBar(gMainProgressBar, edit=True, step=1)
|
||||||
|
|
||||||
|
mc.progressBar(gMainProgressBar, edit=True, endProgress=True)
|
||||||
Loading…
Reference in New Issue