New Addon: Import Autodesk .max #105013
120
io_import_max.py
120
io_import_max.py
@ -14,7 +14,7 @@
|
||||
bl_info = {
|
||||
"name": "Import Autodesk MAX (.max)",
|
||||
"author": "Sebastian Sille, Philippe Lagadec, Jens M. Plonka",
|
||||
"version": (1, 1, 0),
|
||||
"version": (1, 1, 2),
|
||||
"blender": (4, 0, 0),
|
||||
"location": "File > Import",
|
||||
"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,
|
||||
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",
|
||||
description="Use matrix to transform the objects",
|
||||
default=False,
|
||||
@ -70,6 +78,35 @@ class Import_max(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
|
||||
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):
|
||||
bl_space_type = 'FILE_BROWSER'
|
||||
bl_region_type = 'TOOL_PROPS'
|
||||
@ -105,12 +142,14 @@ def menu_func(self, context):
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(Import_max)
|
||||
bpy.utils.register_class(MAX_PT_import_include)
|
||||
bpy.utils.register_class(MAX_PT_import_transform)
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func)
|
||||
|
||||
def unregister():
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func)
|
||||
bpy.utils.unregister_class(MAX_PT_import_transform)
|
||||
bpy.utils.unregister_class(MAX_PT_import_include)
|
||||
bpy.utils.unregister_class(Import_max)
|
||||
|
||||
|
||||
@ -1205,7 +1244,8 @@ def get_color(colors, idx):
|
||||
def get_value(colors, idx):
|
||||
prop = get_property(colors, idx)
|
||||
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 None
|
||||
|
||||
@ -1288,7 +1328,7 @@ def adjust_matrix(obj, node):
|
||||
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
|
||||
shape = bpy.data.meshes.new(name)
|
||||
if (key is not None):
|
||||
@ -1297,13 +1337,13 @@ def create_shape(context, pts, indices, node, key, mtx, mat):
|
||||
if (pts):
|
||||
loopstart = []
|
||||
looplines = loop = 0
|
||||
nbr_faces = len(indices)
|
||||
for fid in range(nbr_faces):
|
||||
nb_faces = len(indices)
|
||||
for fid in range(nb_faces):
|
||||
polyface = indices[fid]
|
||||
looplines += len(polyface)
|
||||
shape.vertices.add(len(pts) // 3)
|
||||
shape.loops.add(looplines)
|
||||
shape.polygons.add(nbr_faces)
|
||||
shape.polygons.add(nb_faces)
|
||||
shape.vertices.foreach_set("co", pts)
|
||||
for vtx in indices:
|
||||
loopstart.append(loop)
|
||||
@ -1317,8 +1357,9 @@ def create_shape(context, pts, indices, node, key, mtx, mat):
|
||||
shape.update()
|
||||
obj = bpy.data.objects.new(name, shape)
|
||||
context.view_layer.active_layer_collection.collection.objects.link(obj)
|
||||
adjust_material(obj, mat)
|
||||
obj.matrix_world = mtx
|
||||
if (umt):
|
||||
adjust_material(obj, mat)
|
||||
return True
|
||||
return True
|
||||
|
||||
@ -1424,11 +1465,14 @@ def get_poly_data(chunk):
|
||||
return polylist
|
||||
|
||||
|
||||
def create_editable_poly(context, node, msh, mat, mtx):
|
||||
coords = point3i = point4i = point6i = pointNi = None
|
||||
def create_editable_poly(context, node, msh, mat, mtx, umt, uvm):
|
||||
coords = point4i = point6i = pointNi = None
|
||||
name = node.get_first(TYP_NAME).data
|
||||
poly = msh.get_first(0x08FE)
|
||||
created = False
|
||||
lidx = []
|
||||
lcrd = []
|
||||
lply = []
|
||||
if (poly):
|
||||
for child in poly.children:
|
||||
if (child.types == 0x0100):
|
||||
@ -1437,20 +1481,34 @@ def create_editable_poly(context, node, msh, mat, mtx):
|
||||
point6i = child.data
|
||||
elif (child.types == 0x011A):
|
||||
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):
|
||||
vertex = get_poly_4p(point4i)
|
||||
if (len(vertex) > 0):
|
||||
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:
|
||||
created = True
|
||||
elif (point6i is not None):
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
poly = msh.get_first(0x08FE)
|
||||
created = False
|
||||
@ -1459,15 +1517,15 @@ def create_editable_mesh(context, node, msh, mat, mtx):
|
||||
clsid_chunk = poly.get_first(0x0912)
|
||||
coords = get_point_array(vertex_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
|
||||
|
||||
|
||||
def create_shell(context, node, shell, mat, mtx):
|
||||
def create_shell(context, node, shell, mat, mtx, umt):
|
||||
name = node.get_first(TYP_NAME).data
|
||||
refs = get_references(shell)
|
||||
msh = refs[-1]
|
||||
created = create_editable_mesh(context, node, msh, mat, mtx)
|
||||
created = create_editable_mesh(context, node, msh, mat, mtx, umt)
|
||||
return created
|
||||
|
||||
|
||||
@ -1477,16 +1535,16 @@ def create_skipable(context, node, skip):
|
||||
return True
|
||||
|
||||
|
||||
def create_mesh(context, node, msh, mtx, mat):
|
||||
def create_mesh(context, node, msh, mtx, mat, umt, uvm):
|
||||
created = False
|
||||
uid = get_guid(msh)
|
||||
msh.geometry = None
|
||||
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):
|
||||
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}):
|
||||
created = create_shell(context, node, msh, mat, mtx)
|
||||
created = create_shell(context, node, msh, mat, mtx, umt)
|
||||
else:
|
||||
skip = SKIPPABLE.get(uid)
|
||||
if (skip is not None):
|
||||
@ -1494,7 +1552,7 @@ def create_mesh(context, node, msh, mtx, mat):
|
||||
return created, uid
|
||||
|
||||
|
||||
def create_object(context, node, mscale, transform):
|
||||
def create_object(context, node, mscale, usemat, uvmesh, transform):
|
||||
parent = get_node_parent(node)
|
||||
node.parent = parent
|
||||
name = get_node_name(node)
|
||||
@ -1509,26 +1567,26 @@ def create_object(context, node, mscale, transform):
|
||||
mtx = create_matrix(prs) @ mscale
|
||||
else:
|
||||
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:
|
||||
if (isinstance(chunk, SceneChunk)):
|
||||
if ((get_guid(chunk) == 0x0001) and (get_super_id(chunk) == 0x0001)):
|
||||
try:
|
||||
create_object(context, chunk, mscale, transform)
|
||||
create_object(context, chunk, mscale, usemat, uvmesh, transform)
|
||||
except Exception as exc:
|
||||
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
|
||||
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)):
|
||||
maxfile = ImportMaxFile(filename)
|
||||
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_class_directory(maxfile, filename)
|
||||
read_video_postqueue(maxfile, filename)
|
||||
read_scene(context, maxfile, filename, mscale, transform)
|
||||
read_scene(context, maxfile, filename, mscale, usemat, uvmesh, transform)
|
||||
else:
|
||||
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)
|
||||
if global_matrix is not None:
|
||||
mscale = global_matrix @ mscale
|
||||
read(context, filepath, mscale, transform=use_apply_matrix)
|
||||
mscale = global_matrix @ mscale
|
||||
|
||||
read(context, filepath, mscale, usemat=use_material, uvmesh=use_uv_mesh, transform=use_apply_matrix)
|
||||
|
||||
return {'FINISHED'}
|
Loading…
Reference in New Issue
Block a user