New Addon: Import Autodesk .max #105013

Closed
Sebastian Sille wants to merge 136 commits from (deleted):nrgsille-import_max into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Showing only changes of commit f62018a92d - Show all commits

View File

@ -161,15 +161,14 @@ def unregister():
MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' MAGIC = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1'
WORD_CLSID = "00020900-0000-0000-C000-000000000046" WORD_CLSID = "00020900-0000-0000-C000-000000000046"
MIN_FILE_SIZE = 1536
UNKNOWN_SIZE = 0x7FFFFFFF
MAXFILE_SIZE = 0x7FFFFFFFFFFFFFFF
MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT
DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT DIFSECT = 0xFFFFFFFC # (-4) denotes a DIFAT sector in a FAT
FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT FATSECT = 0xFFFFFFFD # (-3) denotes a FAT sector in a FAT
ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain ENDOFCHAIN = 0xFFFFFFFE # (-2) end of a virtual stream chain
FREESECT = 0xFFFFFFFF # (-1) unallocated sector FREESECT = 0xFFFFFFFF # (-1) unallocated sector
MAXREGSID = 0xFFFFFFFA # (-6) maximum directory entry ID
NOSTREAM = 0xFFFFFFFF # (-1) unallocated directory entry
UNKNOWN_SIZE = 0x7FFFFFFF
MIN_FILE_SIZE = 1536
STGTY_EMPTY = 0 # empty directory entry STGTY_EMPTY = 0 # empty directory entry
STGTY_STORAGE = 1 # element is a storage object STGTY_STORAGE = 1 # element is a storage object
@ -192,12 +191,15 @@ TYP_NAME = 0x0962
INVALID_NAME = re.compile('^[0-9].*') INVALID_NAME = re.compile('^[0-9].*')
UNPACK_BOX_DATA = struct.Struct('<HIHHBff').unpack_from # Index, int, short, short, byte, float, Length UNPACK_BOX_DATA = struct.Struct('<HIHHBff').unpack_from # Index, int, short, short, byte, float, Length
CONFIG = [] FLOAT_POINT = 0x71F11549498702E7 # Float Wire
CLS_DATA = [] MATRIX_POS = 0xFFEE238A118F7E02 # Position XYZ
DLL_DIR_LIST = [] MATRIX_ROT = 0x3A90416731381913 # Rotation Wire
CLS_DIR3_LIST = [] MATRIX_SCL = 0xFEEE238B118F7C01 # Scale XYZ
VID_PST_QUE = [] EDIT_MESH = 0x00000000E44F10B3 # Editable Mesh
SCENE_LIST = [] EDIT_POLY = 0x192F60981BF8338D # Editable Poly
CORO_MTL = 0x448931dd70be6506 # CoronaMtl
ARCH_MTL = 0x4A16365470B05735 # ArchMtl
VRAY_MTL = 0x7034695C37BF3F2F # VRayMtl
SKIPPABLE = { SKIPPABLE = {
0x0000000000001002: 'Camera', 0x0000000000001002: 'Camera',
@ -222,6 +224,13 @@ SKIPPABLE = {
0x77566F65081F1DFC: 'Plane', 0x77566F65081F1DFC: 'Plane',
} }
CONFIG = []
CLS_DATA = []
DLL_DIR_LIST = []
CLS_DIR3_LIST = []
VID_PST_QUE = []
SCENE_LIST = []
def get_valid_name(name): def get_valid_name(name):
if (INVALID_NAME.match(name)): if (INVALID_NAME.match(name)):
@ -326,7 +335,7 @@ class MaxStream(io.BytesIO):
sector_data = fp.read(sectorsize) sector_data = fp.read(sectorsize)
data.append(sector_data) data.append(sector_data)
try: try:
sect = fat[sect] & 0xFFFFFFFF sect = fat[sect] & FREESECT
except IndexError: except IndexError:
break break
data = b"".join(data) data = b"".join(data)
@ -404,12 +413,12 @@ class MaxFileDirEntry:
next_sect = maxfile.fat[next_sect] next_sect = maxfile.fat[next_sect]
def build_storage_tree(self): def build_storage_tree(self):
if self.sid_child != NOSTREAM: if self.sid_child != FREESECT:
self.append_kids(self.sid_child) self.append_kids(self.sid_child)
self.kids.sort() self.kids.sort()
def append_kids(self, child_sid): def append_kids(self, child_sid):
if child_sid == NOSTREAM: if child_sid == FREESECT:
return return
else: else:
child = self.maxfile._load_direntry(child_sid) child = self.maxfile._load_direntry(child_sid)
@ -575,7 +584,7 @@ class ImportMaxFile:
fat1 = self.sector_array(sect) fat1 = self.sector_array(sect)
isect = None isect = None
for isect in fat1: for isect in fat1:
isect = isect & 0xFFFFFFFF isect = isect & FREESECT
if isect == ENDOFCHAIN or isect == FREESECT: if isect == ENDOFCHAIN or isect == FREESECT:
break break
sector = self.getsect(isect) sector = self.getsect(isect)
@ -923,11 +932,11 @@ class ChunkReader():
def get_next_chunk(self, data, offset, level, number, containerReader, primitiveReader): def get_next_chunk(self, data, offset, level, number, containerReader, primitiveReader):
header = 6 header = 6
typ, siz, = struct.unpack("<Hi", data[offset:offset+header]) typ, siz, = struct.unpack("<Hi", data[offset:offset+header])
chunksize = siz & 0x7FFFFFFF chunksize = siz & UNKNOWN_SIZE
if (siz == 0): if (siz == 0):
siz, = struct.unpack("<q", data[offset+header:offset+header+8]) siz, = struct.unpack("<q", data[offset+header:offset+header+8])
header += 8 header += 8
chunksize = siz & 0x7FFFFFFFFFFFFFFF chunksize = siz & MAXFILE_SIZE
if (siz < 0): if (siz < 0):
chunk = containerReader(typ, chunksize, level, number, primitiveReader) chunk = containerReader(typ, chunksize, level, number, primitiveReader)
else: else:
@ -1103,7 +1112,7 @@ def get_point(floatval, default=0.0):
except: except:
print("SyntaxError: %s - assuming 0.0!\n" %(floatval)) print("SyntaxError: %s - assuming 0.0!\n" %(floatval))
return default return default
if (uid == 0x71F11549498702E7): # Float Wire if (uid == FLOAT_POINT): # Float Wire
flv = get_references(floatval)[0] flv = get_references(floatval)[0]
return get_point(flv) return get_point(flv)
else: else:
@ -1126,7 +1135,7 @@ def get_position(pos):
mtx = mathutils.Matrix.Identity(4) mtx = mathutils.Matrix.Identity(4)
if (pos): if (pos):
uid = get_guid(pos) uid = get_guid(pos)
if (uid == 0xFFEE238A118F7E02): # Position XYZ if (uid == MATRIX_POS): # Position XYZ
position = get_point_3d(pos) position = get_point_3d(pos)
elif (uid == 0x442312): # TCB Position elif (uid == 0x442312): # TCB Position
position = pos.get_first(0x2503).data position = pos.get_first(0x2503).data
@ -1152,7 +1161,7 @@ def get_rotation(pos):
refs = get_references(pos) refs = get_references(pos)
if (len(refs) > 3): if (len(refs) > 3):
return get_rotation(refs[0]) return get_rotation(refs[0])
elif (uid == 0x3A90416731381913): # Rotation Wire elif (uid == MATRIX_ROT): # Rotation Wire
return get_rotation(get_references(pos)[0]) return get_rotation(get_references(pos)[0])
if (rotation): if (rotation):
mtx = mathutils.Matrix.Rotation(rotation.angle, 4, rotation.axis) mtx = mathutils.Matrix.Rotation(rotation.angle, 4, rotation.axis)
@ -1173,7 +1182,7 @@ def get_scale(pos):
if (scale is None): if (scale is None):
scale = pos.get_first(0x2505) scale = pos.get_first(0x2505)
pos = scale.data pos = scale.data
elif (uid == 0xFEEE238B118F7C01): # ScaleXYZ elif (uid == MATRIX_SCL): # ScaleXYZ
pos = get_point_3d(pos, 1.0) pos = get_point_3d(pos, 1.0)
else: else:
return mtx return mtx
@ -1287,6 +1296,7 @@ def get_arch_material(ad):
material = Material() material = Material()
try: try:
material.set('diffuse', get_color(ad, 0x1A)) material.set('diffuse', get_color(ad, 0x1A))
material.set('specular', get_color(ad, 0x02))
except: except:
pass pass
return material return material
@ -1302,10 +1312,10 @@ def adjust_material(obj, mat):
elif (uid == 0x0200): # Multi/Sub-Object elif (uid == 0x0200): # Multi/Sub-Object
refs = get_references(mat) refs = get_references(mat)
material = adjust_material(obj, refs[-1]) material = adjust_material(obj, refs[-1])
elif (uid == 0x7034695C37BF3F2F): # VRayMtl elif (uid == VRAY_MTL): # VRayMtl
refs = get_reference(mat) refs = get_reference(mat)
material = get_vray_material(refs[1]) material = get_vray_material(refs[1])
elif (uid == 0x4A16365470B05735): # Arch elif (uid == ARCH_MTL): # Arch
refs = get_references(mat) refs = get_references(mat)
material = get_arch_material(refs[0]) material = get_arch_material(refs[0])
if (obj is not None) and (material is not None): if (obj is not None) and (material is not None):
@ -1539,9 +1549,9 @@ def create_mesh(context, node, msh, mtx, mat, umt, uvm):
created = False created = False
uid = get_guid(msh) uid = get_guid(msh)
msh.geometry = None msh.geometry = None
if (uid == 0x0E44F10B3): if (uid == EDIT_MESH):
created = create_editable_mesh(context, node, msh, mat, mtx, umt) created = create_editable_mesh(context, node, msh, mat, mtx, umt)
elif (uid == 0x192F60981BF8338D): elif (uid == EDIT_POLY):
created = create_editable_poly(context, node, msh, mat, mtx, umt, uvm) created = create_editable_poly(context, node, msh, mat, mtx, umt, uvm)
elif (uid in {0x2032, 0x2033}): elif (uid in {0x2032, 0x2033}):
created = create_shell(context, node, msh, mat, mtx, umt, uvm) created = create_shell(context, node, msh, mat, mtx, umt, uvm)
@ -1557,7 +1567,7 @@ def create_object(context, node, mscale, usemat, uvmesh, transform):
node.parent = parent node.parent = parent
name = get_node_name(node) name = get_node_name(node)
prs, msh, mat, lyr = get_matrix_mesh_material(node) prs, msh, mat, lyr = get_matrix_mesh_material(node)
while ((parent is not None) and (get_guid(parent) != 0x0002)): while ((parent is not None) and (get_guid(parent) != 0x02)):
name = "%s/%s" %(get_node_name(parent), name) name = "%s/%s" %(get_node_name(parent), name)
parent_mtx = parent.matrix parent_mtx = parent.matrix
if (parent_mtx): if (parent_mtx):
@ -1573,7 +1583,7 @@ def create_object(context, node, mscale, usemat, uvmesh, transform):
def make_scene(context, mscale, usemat, uvmesh, transform, parent, level=0): def make_scene(context, mscale, usemat, uvmesh, transform, parent, level=0):
for chunk in parent.children: for chunk in parent.children:
if (isinstance(chunk, SceneChunk)): if (isinstance(chunk, SceneChunk)):
if ((get_guid(chunk) == 0x0001) and (get_super_id(chunk) == 0x0001)): if ((get_guid(chunk) == 0x01) and (get_super_id(chunk) == 0x01)):
try: try:
create_object(context, chunk, mscale, usemat, uvmesh, transform) create_object(context, chunk, mscale, usemat, uvmesh, transform)
except Exception as exc: except Exception as exc: