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.
3 changed files with 66 additions and 37 deletions
Showing only changes of commit bd9a39823a - Show all commits

View File

@ -55,7 +55,7 @@ class Import3DS(bpy.types.Operator, ImportHelper):
soft_min=0.0, soft_max=1000.0, soft_min=0.0, soft_max=1000.0,
default=10.0, default=10.0,
) )
convert_unit: BoolProperty( use_scene_unit: BoolProperty(
name="Scene Units", name="Scene Units",
description="Converts to scene unit length settings", description="Converts to scene unit length settings",
default=False, default=False,
@ -72,7 +72,7 @@ class Import3DS(bpy.types.Operator, ImportHelper):
('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2), ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2),
('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA', 0x4), ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA', 0x4),
('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA', 0x8), ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA', 0x8),
('EMPTY', "Empty".rjust(11), "", 'EMPTY_DATA', 0x10), ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS', 0x10),
), ),
description="Object types to import", description="Object types to import",
default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
@ -83,7 +83,7 @@ class Import3DS(bpy.types.Operator, ImportHelper):
"importing incorrectly", "importing incorrectly",
default=True, default=True,
) )
read_keyframe: BoolProperty( use_keyframes: BoolProperty(
name="Animation", name="Animation",
description="Read the keyframe data", description="Read the keyframe data",
default=True, default=True,
@ -93,6 +93,11 @@ class Import3DS(bpy.types.Operator, ImportHelper):
description="Transform to matrix world", description="Transform to matrix world",
default=False, default=False,
) )
use_cursor: BoolProperty(
name="Cursor Origin",
description="Read the 3D cursor location",
default=False,
)
def execute(self, context): def execute(self, context):
from . import import_3ds from . import import_3ds
@ -139,8 +144,11 @@ class MAX3DS_PT_import_include(bpy.types.Panel):
layrow.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_image_search else 'IMAGE_DATA') layrow.label(text="", icon='OUTLINER_OB_IMAGE' if operator.use_image_search else 'IMAGE_DATA')
layout.column().prop(operator, "object_filter") layout.column().prop(operator, "object_filter")
layrow = layout.row(align=True) layrow = layout.row(align=True)
layrow.prop(operator, "read_keyframe") layrow.prop(operator, "use_keyframes")
layrow.label(text="", icon='ANIM' if operator.read_keyframe else 'DECORATE_DRIVER') layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
layrow = layout.row(align=True)
layrow.prop(operator, "use_cursor")
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
class MAX3DS_PT_import_transform(bpy.types.Panel): class MAX3DS_PT_import_transform(bpy.types.Panel):
@ -166,8 +174,8 @@ class MAX3DS_PT_import_transform(bpy.types.Panel):
layout.prop(operator, "constrain_size") layout.prop(operator, "constrain_size")
layrow = layout.row(align=True) layrow = layout.row(align=True)
layrow.prop(operator, "convert_unit") layrow.prop(operator, "use_scene_unit")
layrow.label(text="", icon='EMPTY_ARROWS' if operator.convert_unit else 'EMPTY_DATA') layrow.label(text="", icon='EMPTY_ARROWS' if operator.use_scene_unit else 'EMPTY_DATA')
layrow = layout.row(align=True) layrow = layout.row(align=True)
layrow.prop(operator, "use_apply_transform") layrow.prop(operator, "use_apply_transform")
layrow.label(text="", icon='MESH_CUBE' if operator.use_apply_transform else 'MOD_SOLIDIFY') layrow.label(text="", icon='MESH_CUBE' if operator.use_apply_transform else 'MOD_SOLIDIFY')
@ -198,7 +206,7 @@ class Export3DS(bpy.types.Operator, ExportHelper):
soft_min=0.0, soft_max=100000.0, soft_min=0.0, soft_max=100000.0,
default=1.0, default=1.0,
) )
apply_unit: BoolProperty( use_scene_unit: BoolProperty(
name="Scene Units", name="Scene Units",
description="Take the scene unit length settings into account", description="Take the scene unit length settings into account",
default=False, default=False,
@ -214,7 +222,7 @@ class Export3DS(bpy.types.Operator, ExportHelper):
('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2), ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2),
('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA',0x4), ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA',0x4),
('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA',0x8), ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA',0x8),
('EMPTY', "Empty".rjust(11), "", 'EMPTY_DATA',0x10), ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS',0x10),
), ),
description="Object types to export", description="Object types to export",
default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'}, default={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
@ -224,11 +232,16 @@ class Export3DS(bpy.types.Operator, ExportHelper):
description="Export hierarchy chunks", description="Export hierarchy chunks",
default=False, default=False,
) )
write_keyframe: BoolProperty( use_keyframes: BoolProperty(
name="Animation", name="Animation",
description="Write the keyframe data", description="Write the keyframe data",
default=False, default=False,
) )
use_cursor: BoolProperty(
name="Cursor Origin",
description="Save the 3D cursor location",
default=False,
)
def execute(self, context): def execute(self, context):
from . import export_3ds from . import export_3ds
@ -278,9 +291,11 @@ class MAX3DS_PT_export_include(bpy.types.Panel):
layrow.prop(operator, "use_hierarchy") layrow.prop(operator, "use_hierarchy")
layrow.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF') layrow.label(text="", icon='OUTLINER' if operator.use_hierarchy else 'CON_CHILDOF')
layrow = layout.row(align=True) layrow = layout.row(align=True)
layrow.prop(operator, "write_keyframe") layrow.prop(operator, "use_keyframes")
layrow.label(text="", icon='ANIM' if operator.write_keyframe else 'DECORATE_DRIVER') layrow.label(text="", icon='ANIM' if operator.use_keyframes else 'DECORATE_DRIVER')
layout.use_property_split = True layrow = layout.row(align=True)
layrow.prop(operator, "use_cursor")
layrow.label(text="", icon='PIVOT_CURSOR' if operator.use_cursor else 'CURSOR')
class MAX3DS_PT_export_transform(bpy.types.Panel): class MAX3DS_PT_export_transform(bpy.types.Panel):
@ -306,8 +321,8 @@ class MAX3DS_PT_export_transform(bpy.types.Panel):
layout.prop(operator, "scale_factor") layout.prop(operator, "scale_factor")
layrow = layout.row(align=True) layrow = layout.row(align=True)
layrow.prop(operator, "apply_unit") layrow.prop(operator, "use_scene_unit")
layrow.label(text="", icon='EMPTY_ARROWS' if operator.apply_unit else 'EMPTY_DATA') layrow.label(text="", icon='EMPTY_ARROWS' if operator.use_scene_unit else 'EMPTY_DATA')
layout.prop(operator, "axis_forward") layout.prop(operator, "axis_forward")
layout.prop(operator, "axis_up") layout.prop(operator, "axis_up")

View File

@ -36,6 +36,7 @@ SOLIDBACKGND = 0x1200 # The background color (RGB)
USE_SOLIDBGND = 0x1201 # The background color flag USE_SOLIDBGND = 0x1201 # The background color flag
VGRADIENT = 0x1300 # The background gradient colors VGRADIENT = 0x1300 # The background gradient colors
USE_VGRADIENT = 0x1301 # The background gradient flag USE_VGRADIENT = 0x1301 # The background gradient flag
O_CONSTS = 0x1500 # The origin of the 3D cursor
AMBIENTLIGHT = 0x2100 # The color of the ambient light AMBIENTLIGHT = 0x2100 # The color of the ambient light
LAYER_FOG = 0x2302 # The fog atmosphere settings LAYER_FOG = 0x2302 # The fog atmosphere settings
USE_LAYER_FOG = 0x2303 # The fog atmosphere flag USE_LAYER_FOG = 0x2303 # The fog atmosphere flag
@ -1497,8 +1498,8 @@ def make_ambient_node(world):
# EXPORT # # EXPORT #
########## ##########
def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use_selection=False, def save(operator, context, filepath="", scale_factor=1.0, use_scene_unit=False, use_selection=False,
object_filter=None, use_hierarchy=False, write_keyframe=False, global_matrix=None): object_filter=None, use_hierarchy=False, use_keyframes=False, global_matrix=None, use_cursor=False):
"""Save the Blender scene to a 3ds file.""" """Save the Blender scene to a 3ds file."""
# Time the export # Time the export
@ -1511,7 +1512,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
world = scene.world world = scene.world
unit_measure = 1.0 unit_measure = 1.0
if apply_unit: if use_scene_unit:
unit_length = scene.unit_settings.length_unit unit_length = scene.unit_settings.length_unit
if unit_length == 'KILOMETERS': if unit_length == 'KILOMETERS':
unit_measure = 0.001 unit_measure = 0.001
@ -1549,21 +1550,29 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
mscale.add_variable("scale", _3ds_float(1.0)) mscale.add_variable("scale", _3ds_float(1.0))
object_info.add_subchunk(mscale) object_info.add_subchunk(mscale)
# Add 3D cursor location
if use_cursor:
cursor_chunk = _3ds_chunk(O_CONSTS)
cursor_chunk.add_variable("cursor", _3ds_point_3d(scene.cursor.location))
object_info.add_subchunk(cursor_chunk)
# Init main keyframe data chunk # Init main keyframe data chunk
if write_keyframe: if use_keyframes:
revision = 0x0005 revision = 0x0005
stop = scene.frame_end stop = scene.frame_end
start = scene.frame_start start = scene.frame_start
curtime = scene.frame_current curtime = scene.frame_current
kfdata = make_kfdata(revision, start, stop, curtime) kfdata = make_kfdata(revision, start, stop, curtime)
# Add AMBIENT, BACKGROUND and BITMAP # Add AMBIENT color
if world is not None and 'WORLD' in object_filter: if world is not None and 'WORLD' in object_filter:
ambient_chunk = _3ds_chunk(AMBIENTLIGHT) ambient_chunk = _3ds_chunk(AMBIENTLIGHT)
ambient_light = _3ds_chunk(RGB) ambient_light = _3ds_chunk(RGB)
ambient_light.add_variable("ambient", _3ds_float_color(world.color)) ambient_light.add_variable("ambient", _3ds_float_color(world.color))
ambient_chunk.add_subchunk(ambient_light) ambient_chunk.add_subchunk(ambient_light)
object_info.add_subchunk(ambient_chunk) object_info.add_subchunk(ambient_chunk)
# Add BACKGROUND and BITMAP
if world.use_nodes: if world.use_nodes:
ntree = world.node_tree.links ntree = world.node_tree.links
background_color_chunk = _3ds_chunk(RGB) background_color_chunk = _3ds_chunk(RGB)
@ -1604,7 +1613,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
object_info.add_subchunk(fog_chunk) object_info.add_subchunk(fog_chunk)
if layer.use_pass_mist: if layer.use_pass_mist:
object_info.add_subchunk(use_fog_flag) object_info.add_subchunk(use_fog_flag)
if write_keyframe and world.animation_data: if use_keyframes and world.animation_data:
kfdata.add_subchunk(make_ambient_node(world)) kfdata.add_subchunk(make_ambient_node(world))
# Make a list of all materials used in the selected meshes (use dictionary, each material is added once) # Make a list of all materials used in the selected meshes (use dictionary, each material is added once)
@ -1746,13 +1755,13 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
operator.report({'WARNING'}, "Object %r can't be written into a 3DS file") operator.report({'WARNING'}, "Object %r can't be written into a 3DS file")
# Export object node # Export object node
if write_keyframe: if use_keyframes:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id))
i += i i += i
# Create chunks for all empties - only requires a object node # Create chunks for all empties - only requires a object node
if write_keyframe: if use_keyframes:
for ob in empty_objects: for ob in empty_objects:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id))
@ -1816,7 +1825,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
object_info.add_subchunk(object_chunk) object_info.add_subchunk(object_chunk)
# Export light and spotlight target node # Export light and spotlight target node
if write_keyframe: if use_keyframes:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id))
if ob.data.type == 'SPOT': if ob.data.type == 'SPOT':
kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id))
@ -1850,7 +1859,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
object_info.add_subchunk(object_chunk) object_info.add_subchunk(object_chunk)
# Export camera and target node # Export camera and target node
if write_keyframe: if use_keyframes:
kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_object_node(ob, translation, rotation, scale, name_id))
kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id)) kfdata.add_subchunk(make_target_node(ob, translation, rotation, scale, name_id))
@ -1858,7 +1867,7 @@ def save(operator, context, filepath="", scale_factor=1.0, apply_unit=False, use
primary.add_subchunk(object_info) primary.add_subchunk(object_info)
# Add main keyframe data chunk to primary chunk # Add main keyframe data chunk to primary chunk
if write_keyframe: if use_keyframes:
primary.add_subchunk(kfdata) primary.add_subchunk(kfdata)
# The chunk hierarchy is completely built, now check the size # The chunk hierarchy is completely built, now check the size

View File

@ -43,6 +43,7 @@ SOLIDBACKGND = 0x1200 # The background color (RGB)
USE_SOLIDBGND = 0x1201 # The background color flag USE_SOLIDBGND = 0x1201 # The background color flag
VGRADIENT = 0x1300 # The background gradient colors VGRADIENT = 0x1300 # The background gradient colors
USE_VGRADIENT = 0x1301 # The background gradient flag USE_VGRADIENT = 0x1301 # The background gradient flag
O_CONSTS = 0x1500 # The origin of the 3D cursor
AMBIENTLIGHT = 0x2100 # The color of the ambient light AMBIENTLIGHT = 0x2100 # The color of the ambient light
LAYER_FOG = 0x2302 # The fog atmosphere settings LAYER_FOG = 0x2302 # The fog atmosphere settings
USE_LAYER_FOG = 0x2303 # The fog atmosphere flag USE_LAYER_FOG = 0x2303 # The fog atmosphere flag
@ -333,8 +334,8 @@ def add_texture_to_material(image, contextWrapper, pct, extend, alpha, scale, of
childs_list = [] childs_list = []
parent_list = [] parent_list = []
def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAIN, FILTER,
FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE): IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR):
contextObName = None contextObName = None
contextWorld = None contextWorld = None
@ -679,6 +680,10 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
if version > 3: if version > 3:
print("\tNon-Fatal Error: Version greater than 3, may not load correctly: ", version) print("\tNon-Fatal Error: Version greater than 3, may not load correctly: ", version)
# If cursor location
elif CURSOR and new_chunk.ID == O_CONSTS:
context.scene.cursor.location = read_float_array(new_chunk)
# If ambient light chunk # If ambient light chunk
elif CreateWorld and new_chunk.ID == AMBIENTLIGHT: elif CreateWorld and new_chunk.ID == AMBIENTLIGHT:
path, filename = os.path.split(file.name) path, filename = os.path.split(file.name)
@ -768,8 +773,8 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
# is it an object info chunk? # is it an object info chunk?
elif new_chunk.ID == OBJECTINFO: elif new_chunk.ID == OBJECTINFO:
process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, process_next_chunk(context, file, new_chunk, imported_objects, CONSTRAIN, FILTER,
FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR)
# keep track of how much we read in the main chunk # keep track of how much we read in the main chunk
new_chunk.bytes_read += temp_chunk.bytes_read new_chunk.bytes_read += temp_chunk.bytes_read
@ -1416,7 +1421,7 @@ def process_next_chunk(context, file, previous_chunk, imported_objects, CONSTRAI
########## ##########
def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, FILTER=None, def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True, FILTER=None,
WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None): WORLD_MATRIX=False, KEYFRAME=True, APPLY_MATRIX=True, CONVERSE=None, CURSOR=False):
print("importing 3DS: %r..." % (filepath), end="") print("importing 3DS: %r..." % (filepath), end="")
@ -1458,8 +1463,8 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True,
MEASURE = 0.000001 MEASURE = 0.000001
imported_objects = [] # Fill this list with objects imported_objects = [] # Fill this list with objects
process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, process_next_chunk(context, file, current_chunk, imported_objects, CONSTRAIN, FILTER,
FILTER, IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE) IMAGE_SEARCH, WORLD_MATRIX, KEYFRAME, CONVERSE, MEASURE, CURSOR)
# fixme, make unglobal # fixme, make unglobal
object_dictionary.clear() object_dictionary.clear()
@ -1553,12 +1558,12 @@ def load_3ds(filepath, context, CONSTRAIN=10.0, UNITS=False, IMAGE_SEARCH=True,
file.close() file.close()
def load(operator, context, filepath="", constrain_size=0.0, convert_unit=False, def load(operator, context, filepath="", constrain_size=0.0, use_scene_unit=False,
use_image_search=True, object_filter=None, use_world_matrix=False, use_image_search=True, object_filter=None, use_world_matrix=False,
read_keyframe=True, use_apply_transform=True, global_matrix=None,): use_keyframes=True, use_apply_transform=True, global_matrix=None, use_cursor=False):
load_3ds(filepath, context, CONSTRAIN=constrain_size, UNITS=convert_unit, load_3ds(filepath, context, CONSTRAIN=constrain_size, UNITS=use_scene_unit,
IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix, IMAGE_SEARCH=use_image_search, FILTER=object_filter, WORLD_MATRIX=use_world_matrix,
KEYFRAME=read_keyframe, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix,) KEYFRAME=use_keyframes, APPLY_MATRIX=use_apply_transform, CONVERSE=global_matrix, CURSOR=use_cursor,)
return {'FINISHED'} return {'FINISHED'}