New Addon: Import Autodesk .max #105013
104
io_import_max.py
104
io_import_max.py
@ -48,6 +48,18 @@ class Import_max(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
|||||||
filename_ext = ".max"
|
filename_ext = ".max"
|
||||||
filter_glob: bpy.props.StringProperty(default="*.max", options={'HIDDEN'},)
|
filter_glob: bpy.props.StringProperty(default="*.max", options={'HIDDEN'},)
|
||||||
|
|
||||||
|
scale_objects: bpy.props.FloatProperty(name="Scale",
|
||||||
|
description="Scale factor for all objects",
|
||||||
|
min=0.0, max=10000.0,
|
||||||
|
soft_min=0.0, soft_max=10000.0,
|
||||||
|
default=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
use_apply_matrix: bpy.props.BoolProperty(name="Apply Matrix",
|
||||||
|
description="Use matrix to transform the objects",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
keywords = self.as_keywords(ignore=("axis_forward", "axis_up", "filter_glob"))
|
keywords = self.as_keywords(ignore=("axis_forward", "axis_up", "filter_glob"))
|
||||||
global_matrix = axis_conversion(from_forward=self.axis_forward, from_up=self.axis_up,).to_4x4()
|
global_matrix = axis_conversion(from_forward=self.axis_forward, from_up=self.axis_up,).to_4x4()
|
||||||
@ -200,6 +212,11 @@ def _clsid(clsid):
|
|||||||
((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) +
|
((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) +
|
||||||
tuple(map(i8, clsid[8:16]))))
|
tuple(map(i8, clsid[8:16]))))
|
||||||
|
|
||||||
|
|
||||||
|
###############
|
||||||
|
# DATA IMPORT #
|
||||||
|
###############
|
||||||
|
|
||||||
def is_maxfile (filename):
|
def is_maxfile (filename):
|
||||||
"""Test if file is a MAX OLE2 container."""
|
"""Test if file is a MAX OLE2 container."""
|
||||||
if hasattr(filename, 'read'):
|
if hasattr(filename, 'read'):
|
||||||
@ -671,6 +688,10 @@ class ImportMaxFile:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
# DATA PROCESSING #
|
||||||
|
###################
|
||||||
|
|
||||||
class MaxChunk():
|
class MaxChunk():
|
||||||
"""Representing a chunk of a .max file."""
|
"""Representing a chunk of a .max file."""
|
||||||
def __init__(self, types, size, level, number):
|
def __init__(self, types, size, level, number):
|
||||||
@ -1120,6 +1141,24 @@ def create_matrix(prc):
|
|||||||
return mtx
|
return mtx
|
||||||
|
|
||||||
|
|
||||||
|
def get_matrix_mesh_material(node):
|
||||||
|
refs = get_reference(node)
|
||||||
|
if (refs):
|
||||||
|
mtx = refs.get(0, None)
|
||||||
|
msh = refs.get(1, None)
|
||||||
|
mat = refs.get(3, None)
|
||||||
|
lyr = refs.get(6, None)
|
||||||
|
else:
|
||||||
|
refs = get_references(node)
|
||||||
|
mtx = refs[0]
|
||||||
|
msh = refs[1]
|
||||||
|
mat = refs[3]
|
||||||
|
lyr = None
|
||||||
|
if (len(refs) > 6):
|
||||||
|
lyr = refs[6]
|
||||||
|
return mtx, msh, mat, lyr
|
||||||
|
|
||||||
|
|
||||||
def get_property(properties, idx):
|
def get_property(properties, idx):
|
||||||
for child in properties.children:
|
for child in properties.children:
|
||||||
if (child.types & 0x100E):
|
if (child.types & 0x100E):
|
||||||
@ -1216,12 +1255,18 @@ def adjust_material(obj, mat):
|
|||||||
objMaterial.roughness = 1.0 - material.get('shinines', 0.6)
|
objMaterial.roughness = 1.0 - material.get('shinines', 0.6)
|
||||||
|
|
||||||
|
|
||||||
def create_shape(context, pts, indices, node, key, prc, mat):
|
def adjust_matrix(obj, node):
|
||||||
|
mtx = create_matrix(node).flatten()
|
||||||
|
plc = mathutils.Matrix(*mtx)
|
||||||
|
obj.matrix_world = plc
|
||||||
|
return plc
|
||||||
|
|
||||||
|
|
||||||
|
def create_shape(context, pts, indices, node, key, mtx, mat):
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
shape = bpy.data.meshes.new(name)
|
shape = bpy.data.meshes.new(name)
|
||||||
if (key is not None):
|
if (key is not None):
|
||||||
name = "%s_%d" %(name, key)
|
name = "%s_%d" %(name, key)
|
||||||
mtx = create_matrix(prc)
|
|
||||||
data = []
|
data = []
|
||||||
if (pts):
|
if (pts):
|
||||||
loopstart = []
|
loopstart = []
|
||||||
@ -1247,6 +1292,7 @@ def create_shape(context, pts, indices, node, key, prc, mat):
|
|||||||
obj = bpy.data.objects.new(name, shape)
|
obj = bpy.data.objects.new(name, shape)
|
||||||
context.view_layer.active_layer_collection.collection.objects.link(obj)
|
context.view_layer.active_layer_collection.collection.objects.link(obj)
|
||||||
adjust_material(obj, mat)
|
adjust_material(obj, mat)
|
||||||
|
obj.matrix_world = mtx
|
||||||
return True
|
return True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -1391,31 +1437,6 @@ def create_editable_mesh(context, node, msh, mat, mtx):
|
|||||||
return created
|
return created
|
||||||
|
|
||||||
|
|
||||||
def get_matrix_mesh_material(node):
|
|
||||||
refs = get_reference(node)
|
|
||||||
if (refs):
|
|
||||||
mtx = refs.get(0, None)
|
|
||||||
msh = refs.get(1, None)
|
|
||||||
mat = refs.get(3, None)
|
|
||||||
lyr = refs.get(6, None)
|
|
||||||
else:
|
|
||||||
refs = get_references(node)
|
|
||||||
mtx = refs[0]
|
|
||||||
msh = refs[1]
|
|
||||||
mat = refs[3]
|
|
||||||
lyr = None
|
|
||||||
if (len(refs) > 6):
|
|
||||||
lyr = refs[6]
|
|
||||||
return mtx, msh, mat, lyr
|
|
||||||
|
|
||||||
|
|
||||||
def adjust_matrix(obj, node):
|
|
||||||
mtx = create_matrix(node).flatten()
|
|
||||||
plc = mathutils.Matrix(*mtx)
|
|
||||||
obj.matrix_world = plc
|
|
||||||
return plc
|
|
||||||
|
|
||||||
|
|
||||||
def create_shell(context, node, shell, mat, mtx):
|
def create_shell(context, node, shell, mat, mtx):
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
refs = get_references(shell)
|
refs = get_references(shell)
|
||||||
@ -1424,7 +1445,7 @@ def create_shell(context, node, shell, mat, mtx):
|
|||||||
return created
|
return created
|
||||||
|
|
||||||
|
|
||||||
def create_skipable(context, node, msh, mat, mtx, skip):
|
def create_skipable(context, node, skip):
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
print(" skipping %s '%s'... " %(skip, name))
|
print(" skipping %s '%s'... " %(skip, name))
|
||||||
return True
|
return True
|
||||||
@ -1443,11 +1464,11 @@ def create_mesh(context, node, msh, mtx, mat):
|
|||||||
else:
|
else:
|
||||||
skip = SKIPPABLE.get(uid)
|
skip = SKIPPABLE.get(uid)
|
||||||
if (skip is not None):
|
if (skip is not None):
|
||||||
created = create_skipable(context, node, msh, mat, mtx, skip)
|
created = create_skipable(context, node, skip)
|
||||||
return created, uid
|
return created, uid
|
||||||
|
|
||||||
|
|
||||||
def create_object(context, node):
|
def create_object(context, node, mscale, transform):
|
||||||
parent = get_node_parent(node)
|
parent = get_node_parent(node)
|
||||||
node.parent = parent
|
node.parent = parent
|
||||||
name = get_node_name(node)
|
name = get_node_name(node)
|
||||||
@ -1458,26 +1479,30 @@ def create_object(context, node):
|
|||||||
if (parent_mtx):
|
if (parent_mtx):
|
||||||
mtx = mtx.dot(parent_mtx)
|
mtx = mtx.dot(parent_mtx)
|
||||||
parent = get_node_parent(parent)
|
parent = get_node_parent(parent)
|
||||||
|
if (transform):
|
||||||
|
mtx = create_matrix(mtx) @ mscale
|
||||||
|
else:
|
||||||
|
mtx = mscale
|
||||||
created, uid = create_mesh(context, node, msh, mtx, mat)
|
created, uid = create_mesh(context, node, msh, mtx, mat)
|
||||||
|
|
||||||
|
|
||||||
def make_scene(context, parent, level=0):
|
def make_scene(context, mscale, 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) == 0x0001) and (get_super_id(chunk) == 0x0001)):
|
||||||
try:
|
try:
|
||||||
create_object(context, chunk)
|
create_object(context, chunk, mscale, transform)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print('ImportError:', exc, chunk)
|
print('ImportError:', exc, chunk)
|
||||||
|
|
||||||
|
|
||||||
def read_scene(context, maxfile, filename):
|
def read_scene(context, maxfile, filename, mscale, transform):
|
||||||
global SCENE_LIST
|
global SCENE_LIST
|
||||||
SCENE_LIST = read_chunks(maxfile, 'Scene', filename+'.Scn.bin', containerReader=SceneChunk)
|
SCENE_LIST = read_chunks(maxfile, 'Scene', filename+'.Scn.bin', containerReader=SceneChunk)
|
||||||
make_scene(context, SCENE_LIST[0], 0)
|
make_scene(context, mscale, transform, SCENE_LIST[0], 0)
|
||||||
|
|
||||||
|
|
||||||
def read(context, filename):
|
def read(context, filename, mscale, transform):
|
||||||
if (is_maxfile(filename)):
|
if (is_maxfile(filename)):
|
||||||
maxfile = ImportMaxFile(filename)
|
maxfile = ImportMaxFile(filename)
|
||||||
prop = maxfile.getproperties('\x05DocumentSummaryInformation', convert_time=True, no_conversion=[10])
|
prop = maxfile.getproperties('\x05DocumentSummaryInformation', convert_time=True, no_conversion=[10])
|
||||||
@ -1487,12 +1512,15 @@ def read(context, filename):
|
|||||||
read_directory(maxfile, filename)
|
read_directory(maxfile, filename)
|
||||||
read_class_directory(maxfile, filename)
|
read_class_directory(maxfile, filename)
|
||||||
read_video_postqueue(maxfile, filename)
|
read_video_postqueue(maxfile, filename)
|
||||||
read_scene(context, maxfile, filename)
|
read_scene(context, maxfile, filename, mscale, transform)
|
||||||
else:
|
else:
|
||||||
print("File seems to be no 3D Studio Max file!")
|
print("File seems to be no 3D Studio Max file!")
|
||||||
|
|
||||||
|
|
||||||
def load(operator, context, filepath="", global_matrix=None):
|
def load(operator, context, filepath="", scale_objects=1.0, use_apply_matrix=False, global_matrix=None):
|
||||||
read(context, filepath)
|
mscale = mathutils.Matrix.Scale(scale_objects, 4)
|
||||||
|
if global_matrix is not None:
|
||||||
|
mscale = global_matrix @ mscale
|
||||||
|
read(context, filepath, mscale, transform=use_apply_matrix)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
Loading…
Reference in New Issue
Block a user