New Addon: Import Autodesk .max #105013
118
io_import_max.py
118
io_import_max.py
@ -14,7 +14,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Import Autodesk MAX (.max)",
|
"name": "Import Autodesk MAX (.max)",
|
||||||
"author": "Sebastian Sille, Philippe Lagadec, Jens M. Plonka",
|
"author": "Sebastian Sille, Philippe Lagadec, Jens M. Plonka",
|
||||||
"version": (1, 1, 0),
|
"version": (1, 1, 2),
|
||||||
"blender": (4, 0, 0),
|
"blender": (4, 0, 0),
|
||||||
"location": "File > Import",
|
"location": "File > Import",
|
||||||
"description": "Import 3DSMAX meshes & materials",
|
"description": "Import 3DSMAX meshes & materials",
|
||||||
@ -54,6 +54,14 @@ class Import_max(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
|||||||
soft_min=0.0, soft_max=10000.0,
|
soft_min=0.0, soft_max=10000.0,
|
||||||
default=1.0,
|
default=1.0,
|
||||||
)
|
)
|
||||||
|
use_material: bpy.props.BoolProperty(name="Materials",
|
||||||
|
description="Import the materials of the objects",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
use_uv_mesh: bpy.props.BoolProperty(name="UV Mesh",
|
||||||
|
description="Import texture coordinates as mesh objects",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
use_apply_matrix: bpy.props.BoolProperty(name="Apply Matrix",
|
use_apply_matrix: bpy.props.BoolProperty(name="Apply Matrix",
|
||||||
description="Use matrix to transform the objects",
|
description="Use matrix to transform the objects",
|
||||||
default=False,
|
default=False,
|
||||||
@ -70,6 +78,35 @@ class Import_max(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MAX_PT_import_include(bpy.types.Panel):
|
||||||
|
bl_space_type = 'FILE_BROWSER'
|
||||||
|
bl_region_type = 'TOOL_PROPS'
|
||||||
|
bl_label = "Include"
|
||||||
|
bl_parent_id = "FILE_PT_operator"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sfile = context.space_data
|
||||||
|
operator = sfile.active_operator
|
||||||
|
|
||||||
|
return operator.bl_idname == "IMPORT_AUTODESK_OT_max"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = True
|
||||||
|
|
||||||
|
sfile = context.space_data
|
||||||
|
operator = sfile.active_operator
|
||||||
|
|
||||||
|
layrow = layout.row(align=True)
|
||||||
|
layrow.prop(operator, "use_material")
|
||||||
|
layrow.label(text="", icon='MATERIAL' if operator.use_material else 'SHADING_TEXTURE')
|
||||||
|
layrow = layout.row(align=True)
|
||||||
|
layrow.prop(operator, "use_uv_mesh")
|
||||||
|
layrow.label(text="", icon='UV' if operator.use_uv_mesh else 'GROUP_UVS')
|
||||||
|
|
||||||
|
|
||||||
class MAX_PT_import_transform(bpy.types.Panel):
|
class MAX_PT_import_transform(bpy.types.Panel):
|
||||||
bl_space_type = 'FILE_BROWSER'
|
bl_space_type = 'FILE_BROWSER'
|
||||||
bl_region_type = 'TOOL_PROPS'
|
bl_region_type = 'TOOL_PROPS'
|
||||||
@ -105,12 +142,14 @@ def menu_func(self, context):
|
|||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.utils.register_class(Import_max)
|
bpy.utils.register_class(Import_max)
|
||||||
|
bpy.utils.register_class(MAX_PT_import_include)
|
||||||
bpy.utils.register_class(MAX_PT_import_transform)
|
bpy.utils.register_class(MAX_PT_import_transform)
|
||||||
bpy.types.TOPBAR_MT_file_import.append(menu_func)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func)
|
bpy.types.TOPBAR_MT_file_import.remove(menu_func)
|
||||||
bpy.utils.unregister_class(MAX_PT_import_transform)
|
bpy.utils.unregister_class(MAX_PT_import_transform)
|
||||||
|
bpy.utils.unregister_class(MAX_PT_import_include)
|
||||||
bpy.utils.unregister_class(Import_max)
|
bpy.utils.unregister_class(Import_max)
|
||||||
|
|
||||||
|
|
||||||
@ -1205,7 +1244,8 @@ def get_color(colors, idx):
|
|||||||
def get_value(colors, idx):
|
def get_value(colors, idx):
|
||||||
prop = get_property(colors, idx)
|
prop = get_property(colors, idx)
|
||||||
if (prop is not None):
|
if (prop is not None):
|
||||||
val, offset = get_float(prop.data, 15)
|
siz = 15 if (len(prop.data) > 23) else 6
|
||||||
|
val, offset = get_float(prop.data, siz)
|
||||||
return val
|
return val
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -1288,7 +1328,7 @@ def adjust_matrix(obj, node):
|
|||||||
return plc
|
return plc
|
||||||
|
|
||||||
|
|
||||||
def create_shape(context, pts, indices, node, key, mtx, mat):
|
def create_shape(context, pts, indices, node, key, mtx, mat, umt):
|
||||||
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):
|
||||||
@ -1297,13 +1337,13 @@ def create_shape(context, pts, indices, node, key, mtx, mat):
|
|||||||
if (pts):
|
if (pts):
|
||||||
loopstart = []
|
loopstart = []
|
||||||
looplines = loop = 0
|
looplines = loop = 0
|
||||||
nbr_faces = len(indices)
|
nb_faces = len(indices)
|
||||||
for fid in range(nbr_faces):
|
for fid in range(nb_faces):
|
||||||
polyface = indices[fid]
|
polyface = indices[fid]
|
||||||
looplines += len(polyface)
|
looplines += len(polyface)
|
||||||
shape.vertices.add(len(pts) // 3)
|
shape.vertices.add(len(pts) // 3)
|
||||||
shape.loops.add(looplines)
|
shape.loops.add(looplines)
|
||||||
shape.polygons.add(nbr_faces)
|
shape.polygons.add(nb_faces)
|
||||||
shape.vertices.foreach_set("co", pts)
|
shape.vertices.foreach_set("co", pts)
|
||||||
for vtx in indices:
|
for vtx in indices:
|
||||||
loopstart.append(loop)
|
loopstart.append(loop)
|
||||||
@ -1317,8 +1357,9 @@ def create_shape(context, pts, indices, node, key, mtx, mat):
|
|||||||
shape.update()
|
shape.update()
|
||||||
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)
|
|
||||||
obj.matrix_world = mtx
|
obj.matrix_world = mtx
|
||||||
|
if (umt):
|
||||||
|
adjust_material(obj, mat)
|
||||||
return True
|
return True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -1424,11 +1465,14 @@ def get_poly_data(chunk):
|
|||||||
return polylist
|
return polylist
|
||||||
|
|
||||||
|
|
||||||
def create_editable_poly(context, node, msh, mat, mtx):
|
def create_editable_poly(context, node, msh, mat, mtx, umt, uvm):
|
||||||
coords = point3i = point4i = point6i = pointNi = None
|
coords = point4i = point6i = pointNi = None
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
poly = msh.get_first(0x08FE)
|
poly = msh.get_first(0x08FE)
|
||||||
created = False
|
created = False
|
||||||
|
lidx = []
|
||||||
|
lcrd = []
|
||||||
|
lply = []
|
||||||
if (poly):
|
if (poly):
|
||||||
for child in poly.children:
|
for child in poly.children:
|
||||||
if (child.types == 0x0100):
|
if (child.types == 0x0100):
|
||||||
@ -1437,20 +1481,34 @@ def create_editable_poly(context, node, msh, mat, mtx):
|
|||||||
point6i = child.data
|
point6i = child.data
|
||||||
elif (child.types == 0x011A):
|
elif (child.types == 0x011A):
|
||||||
point4i = calc_point_3d(child)
|
point4i = calc_point_3d(child)
|
||||||
|
elif (child.types == 0x0310):
|
||||||
|
pointNi = child.data
|
||||||
|
elif (child.types == 0x0124):
|
||||||
|
lidx.append(get_long(child.data, 0)[0])
|
||||||
|
elif (child.types == 0x0128):
|
||||||
|
lcrd.append(calc_point_float(child.data))
|
||||||
|
elif (child.types == 0x012B):
|
||||||
|
lply.append(get_poly_data(child))
|
||||||
if (point4i is not None):
|
if (point4i is not None):
|
||||||
vertex = get_poly_4p(point4i)
|
vertex = get_poly_4p(point4i)
|
||||||
if (len(vertex) > 0):
|
if (len(vertex) > 0):
|
||||||
for key, ngons in vertex.items():
|
for key, ngons in vertex.items():
|
||||||
created |= create_shape(context, coords, ngons, node, key, mtx, mat)
|
created |= create_shape(context, coords, ngons, node, key, mtx, mat, umt)
|
||||||
else:
|
else:
|
||||||
created = True
|
created = True
|
||||||
elif (point6i is not None):
|
elif (point6i is not None):
|
||||||
ngons = get_poly_6p(point6i)
|
ngons = get_poly_6p(point6i)
|
||||||
created = create_shape(context, coords, ngons, node, None, mtx, mat)
|
created = create_shape(context, coords, ngons, node, None, mtx, mat, umt)
|
||||||
|
elif (pointNi is not None):
|
||||||
|
ngons = get_poly_5p(pointNi)
|
||||||
|
created = create_shape(context, coords, ngons, node, None, mtx, mat, umt)
|
||||||
|
if (uvm and len(lidx) > 0):
|
||||||
|
for i in range(len(lidx)):
|
||||||
|
created |= create_shape(context, lcrd[i], lply[i], node, lidx[i], mtx, mat, umt)
|
||||||
return created
|
return created
|
||||||
|
|
||||||
|
|
||||||
def create_editable_mesh(context, node, msh, mat, mtx):
|
def create_editable_mesh(context, node, msh, mat, mtx, umt):
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
poly = msh.get_first(0x08FE)
|
poly = msh.get_first(0x08FE)
|
||||||
created = False
|
created = False
|
||||||
@ -1459,15 +1517,15 @@ def create_editable_mesh(context, node, msh, mat, mtx):
|
|||||||
clsid_chunk = poly.get_first(0x0912)
|
clsid_chunk = poly.get_first(0x0912)
|
||||||
coords = get_point_array(vertex_chunk.data)
|
coords = get_point_array(vertex_chunk.data)
|
||||||
ngons = get_poly_5p(clsid_chunk.data)
|
ngons = get_poly_5p(clsid_chunk.data)
|
||||||
created = create_shape(context, coords, ngons, node, None, mtx, mat)
|
created = create_shape(context, coords, ngons, node, None, mtx, mat, umt)
|
||||||
return created
|
return created
|
||||||
|
|
||||||
|
|
||||||
def create_shell(context, node, shell, mat, mtx):
|
def create_shell(context, node, shell, mat, mtx, umt):
|
||||||
name = node.get_first(TYP_NAME).data
|
name = node.get_first(TYP_NAME).data
|
||||||
refs = get_references(shell)
|
refs = get_references(shell)
|
||||||
msh = refs[-1]
|
msh = refs[-1]
|
||||||
created = create_editable_mesh(context, node, msh, mat, mtx)
|
created = create_editable_mesh(context, node, msh, mat, mtx, umt)
|
||||||
return created
|
return created
|
||||||
|
|
||||||
|
|
||||||
@ -1477,16 +1535,16 @@ def create_skipable(context, node, skip):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def create_mesh(context, node, msh, mtx, mat):
|
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 == 0x0E44F10B3):
|
||||||
created = create_editable_mesh(context, node, msh, mat, mtx)
|
created = create_editable_mesh(context, node, msh, mat, mtx, umt)
|
||||||
elif (uid == 0x192F60981BF8338D):
|
elif (uid == 0x192F60981BF8338D):
|
||||||
created = create_editable_poly(context, node, msh, mat, mtx)
|
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)
|
created = create_shell(context, node, msh, mat, mtx, umt)
|
||||||
else:
|
else:
|
||||||
skip = SKIPPABLE.get(uid)
|
skip = SKIPPABLE.get(uid)
|
||||||
if (skip is not None):
|
if (skip is not None):
|
||||||
@ -1494,7 +1552,7 @@ def create_mesh(context, node, msh, mtx, mat):
|
|||||||
return created, uid
|
return created, uid
|
||||||
|
|
||||||
|
|
||||||
def create_object(context, node, mscale, transform):
|
def create_object(context, node, mscale, usemat, uvmesh, 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)
|
||||||
@ -1509,26 +1567,26 @@ def create_object(context, node, mscale, transform):
|
|||||||
mtx = create_matrix(prs) @ mscale
|
mtx = create_matrix(prs) @ mscale
|
||||||
else:
|
else:
|
||||||
mtx = mscale
|
mtx = mscale
|
||||||
created, uid = create_mesh(context, node, msh, mtx, mat)
|
created, uid = create_mesh(context, node, msh, mtx, mat, usemat, uvmesh)
|
||||||
|
|
||||||
|
|
||||||
def make_scene(context, mscale, 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) == 0x0001) and (get_super_id(chunk) == 0x0001)):
|
||||||
try:
|
try:
|
||||||
create_object(context, chunk, mscale, transform)
|
create_object(context, chunk, mscale, usemat, uvmesh, transform)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print('ImportError:', exc, chunk)
|
print('ImportError:', exc, chunk)
|
||||||
|
|
||||||
|
|
||||||
def read_scene(context, maxfile, filename, mscale, transform):
|
def read_scene(context, maxfile, filename, mscale, usemat, uvmesh, 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, mscale, transform, SCENE_LIST[0], 0)
|
make_scene(context, mscale, usemat, uvmesh, transform, SCENE_LIST[0], 0)
|
||||||
|
|
||||||
|
|
||||||
def read(context, filename, mscale, transform):
|
def read(context, filename, mscale, usemat, uvmesh, 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])
|
||||||
@ -1538,15 +1596,17 @@ def read(context, filename, mscale, transform):
|
|||||||
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, mscale, transform)
|
read_scene(context, maxfile, filename, mscale, usemat, uvmesh, 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="", scale_objects=1.0, use_apply_matrix=False, global_matrix=None):
|
def load(operator, context, filepath="", scale_objects=1.0, use_material=True,
|
||||||
|
use_uv_mesh=False, use_apply_matrix=False, global_matrix=None):
|
||||||
mscale = mathutils.Matrix.Scale(scale_objects, 4)
|
mscale = mathutils.Matrix.Scale(scale_objects, 4)
|
||||||
if global_matrix is not None:
|
if global_matrix is not None:
|
||||||
mscale = global_matrix @ mscale
|
mscale = global_matrix @ mscale
|
||||||
read(context, filepath, mscale, transform=use_apply_matrix)
|
|
||||||
|
read(context, filepath, mscale, usemat=use_material, uvmesh=use_uv_mesh, transform=use_apply_matrix)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
Loading…
Reference in New Issue
Block a user