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 b0e40301ce - Show all commits

View File

@ -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'}