1 # SPDX-FileCopyrightText: 2011-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 from bpy_extras
.io_utils
import (
11 from bpy
.props
import (
19 "name": "Autodesk 3DS format",
20 "author": "Bob Holcomb, Campbell Barton, Sebastian Schrand",
23 "location": "File > Import-Export",
24 "description": "3DS Import/Export meshes, UVs, materials, textures, "
25 "cameras, lamps & animation",
26 "warning": "Images must be in file folder, "
27 "filenames are limited to DOS 8.3 format",
28 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/scene_3ds.html",
29 "category": "Import-Export",
34 if "import_3ds" in locals():
35 importlib
.reload(import_3ds
)
36 if "export_3ds" in locals():
37 importlib
.reload(export_3ds
)
40 @orientation_helper(axis_forward
='Y', axis_up
='Z')
41 class Import3DS(bpy
.types
.Operator
, ImportHelper
):
42 """Import from 3DS file format (.3ds)"""
43 bl_idname
= "import_scene.max3ds"
44 bl_label
= 'Import 3DS'
45 bl_options
= {'PRESET', 'UNDO'}
48 filter_glob
: StringProperty(default
="*.3ds", options
={'HIDDEN'})
50 constrain_size
: FloatProperty(
51 name
="Constrain Size",
52 description
="Scale the model by 10 until it reaches the "
53 "size constraint (0 to disable)",
55 soft_min
=0.0, soft_max
=1000.0,
58 use_scene_unit
: BoolProperty(
60 description
="Convert to scene unit length settings",
63 use_center_pivot
: BoolProperty(
65 description
="Move all geometry to pivot origin",
68 use_image_search
: BoolProperty(
70 description
="Search subdirectories for any associated images "
71 "(Warning, may be slow)",
74 object_filter
: EnumProperty(
75 name
="Object Filter", options
={'ENUM_FLAG'},
76 items
=(('WORLD', "World".rjust(11), "", 'WORLD_DATA', 0x1),
77 ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2),
78 ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA', 0x4),
79 ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA', 0x8),
80 ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS', 0x10),
82 description
="Object types to import",
83 default
={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
85 use_apply_transform
: BoolProperty(
86 name
="Apply Transform",
87 description
="Workaround for object transformations "
88 "importing incorrectly",
91 use_keyframes
: BoolProperty(
93 description
="Read the keyframe data",
96 use_world_matrix
: BoolProperty(
98 description
="Transform to matrix world",
101 use_cursor
: BoolProperty(
102 name
="Cursor Origin",
103 description
="Read the 3D cursor location",
107 def execute(self
, context
):
108 from . import import_3ds
110 keywords
= self
.as_keywords(ignore
=("axis_forward",
115 global_matrix
= axis_conversion(from_forward
=self
.axis_forward
,
116 from_up
=self
.axis_up
,
118 keywords
["global_matrix"] = global_matrix
120 return import_3ds
.load(self
, context
, **keywords
)
122 def draw(self
, context
):
126 class MAX3DS_PT_import_include(bpy
.types
.Panel
):
127 bl_space_type
= 'FILE_BROWSER'
128 bl_region_type
= 'TOOL_PROPS'
130 bl_parent_id
= "FILE_PT_operator"
133 def poll(cls
, context
):
134 sfile
= context
.space_data
135 operator
= sfile
.active_operator
137 return operator
.bl_idname
== "IMPORT_SCENE_OT_max3ds"
139 def draw(self
, context
):
141 layout
.use_property_split
= True
142 layout
.use_property_decorate
= False
144 sfile
= context
.space_data
145 operator
= sfile
.active_operator
147 layrow
= layout
.row(align
=True)
148 layrow
.prop(operator
, "use_image_search")
149 layrow
.label(text
="", icon
='OUTLINER_OB_IMAGE' if operator
.use_image_search
else 'IMAGE_DATA')
150 layout
.column().prop(operator
, "object_filter")
151 layrow
= layout
.row(align
=True)
152 layrow
.prop(operator
, "use_keyframes")
153 layrow
.label(text
="", icon
='ANIM' if operator
.use_keyframes
else 'DECORATE_DRIVER')
154 layrow
= layout
.row(align
=True)
155 layrow
.prop(operator
, "use_cursor")
156 layrow
.label(text
="", icon
='PIVOT_CURSOR' if operator
.use_cursor
else 'CURSOR')
159 class MAX3DS_PT_import_transform(bpy
.types
.Panel
):
160 bl_space_type
= 'FILE_BROWSER'
161 bl_region_type
= 'TOOL_PROPS'
162 bl_label
= "Transform"
163 bl_parent_id
= "FILE_PT_operator"
166 def poll(cls
, context
):
167 sfile
= context
.space_data
168 operator
= sfile
.active_operator
170 return operator
.bl_idname
== "IMPORT_SCENE_OT_max3ds"
172 def draw(self
, context
):
174 layout
.use_property_split
= True
175 layout
.use_property_decorate
= False
177 sfile
= context
.space_data
178 operator
= sfile
.active_operator
180 layout
.prop(operator
, "constrain_size")
181 layrow
= layout
.row(align
=True)
182 layrow
.prop(operator
, "use_scene_unit")
183 layrow
.label(text
="", icon
='EMPTY_ARROWS' if operator
.use_scene_unit
else 'EMPTY_DATA')
184 layrow
= layout
.row(align
=True)
185 layrow
.prop(operator
, "use_center_pivot")
186 layrow
.label(text
="", icon
='OVERLAY' if operator
.use_center_pivot
else 'PIVOT_ACTIVE')
187 layrow
= layout
.row(align
=True)
188 layrow
.prop(operator
, "use_apply_transform")
189 layrow
.label(text
="", icon
='MESH_CUBE' if operator
.use_apply_transform
else 'MOD_SOLIDIFY')
190 layrow
= layout
.row(align
=True)
191 layrow
.prop(operator
, "use_world_matrix")
192 layrow
.label(text
="", icon
='WORLD' if operator
.use_world_matrix
else 'META_BALL')
193 layout
.prop(operator
, "axis_forward")
194 layout
.prop(operator
, "axis_up")
197 @orientation_helper(axis_forward
='Y', axis_up
='Z')
198 class Export3DS(bpy
.types
.Operator
, ExportHelper
):
199 """Export to 3DS file format (.3ds)"""
200 bl_idname
= "export_scene.max3ds"
201 bl_label
= 'Export 3DS'
202 bl_options
= {'PRESET', 'UNDO'}
204 filename_ext
= ".3ds"
205 filter_glob
: StringProperty(
210 scale_factor
: FloatProperty(
212 description
="Master scale factor for all objects",
213 min=0.0, max=100000.0,
214 soft_min
=0.0, soft_max
=100000.0,
217 use_scene_unit
: BoolProperty(
219 description
="Take the scene unit length settings into account",
222 use_selection
: BoolProperty(
224 description
="Export selected objects only",
227 object_filter
: EnumProperty(
228 name
="Object Filter", options
={'ENUM_FLAG'},
229 items
=(('WORLD', "World".rjust(11), "", 'WORLD_DATA',0x1),
230 ('MESH', "Mesh".rjust(11), "", 'MESH_DATA', 0x2),
231 ('LIGHT', "Light".rjust(12), "", 'LIGHT_DATA',0x4),
232 ('CAMERA', "Camera".rjust(11), "", 'CAMERA_DATA',0x8),
233 ('EMPTY', "Empty".rjust(11), "", 'EMPTY_AXIS',0x10),
235 description
="Object types to export",
236 default
={'WORLD', 'MESH', 'LIGHT', 'CAMERA', 'EMPTY'},
238 use_hierarchy
: BoolProperty(
240 description
="Export hierarchy chunks",
243 use_keyframes
: BoolProperty(
245 description
="Write the keyframe data",
248 use_cursor
: BoolProperty(
249 name
="Cursor Origin",
250 description
="Save the 3D cursor location",
254 def execute(self
, context
):
255 from . import export_3ds
257 keywords
= self
.as_keywords(ignore
=("axis_forward",
262 global_matrix
= axis_conversion(to_forward
=self
.axis_forward
,
265 keywords
["global_matrix"] = global_matrix
267 return export_3ds
.save(self
, context
, **keywords
)
269 def draw(self
, context
):
273 class MAX3DS_PT_export_include(bpy
.types
.Panel
):
274 bl_space_type
= 'FILE_BROWSER'
275 bl_region_type
= 'TOOL_PROPS'
277 bl_parent_id
= "FILE_PT_operator"
280 def poll(cls
, context
):
281 sfile
= context
.space_data
282 operator
= sfile
.active_operator
284 return operator
.bl_idname
== "EXPORT_SCENE_OT_max3ds"
286 def draw(self
, context
):
288 layout
.use_property_split
= True
289 layout
.use_property_decorate
= False
291 sfile
= context
.space_data
292 operator
= sfile
.active_operator
294 layrow
= layout
.row(align
=True)
295 layrow
.prop(operator
, "use_selection")
296 layrow
.label(text
="", icon
='RESTRICT_SELECT_OFF' if operator
.use_selection
else 'RESTRICT_SELECT_ON')
297 layout
.column().prop(operator
, "object_filter")
298 layrow
= layout
.row(align
=True)
299 layrow
.prop(operator
, "use_hierarchy")
300 layrow
.label(text
="", icon
='OUTLINER' if operator
.use_hierarchy
else 'CON_CHILDOF')
301 layrow
= layout
.row(align
=True)
302 layrow
.prop(operator
, "use_keyframes")
303 layrow
.label(text
="", icon
='ANIM' if operator
.use_keyframes
else 'DECORATE_DRIVER')
304 layrow
= layout
.row(align
=True)
305 layrow
.prop(operator
, "use_cursor")
306 layrow
.label(text
="", icon
='PIVOT_CURSOR' if operator
.use_cursor
else 'CURSOR')
309 class MAX3DS_PT_export_transform(bpy
.types
.Panel
):
310 bl_space_type
= 'FILE_BROWSER'
311 bl_region_type
= 'TOOL_PROPS'
312 bl_label
= "Transform"
313 bl_parent_id
= "FILE_PT_operator"
316 def poll(cls
, context
):
317 sfile
= context
.space_data
318 operator
= sfile
.active_operator
320 return operator
.bl_idname
== "EXPORT_SCENE_OT_max3ds"
322 def draw(self
, context
):
324 layout
.use_property_split
= True
325 layout
.use_property_decorate
= False
327 sfile
= context
.space_data
328 operator
= sfile
.active_operator
330 layout
.prop(operator
, "scale_factor")
331 layrow
= layout
.row(align
=True)
332 layrow
.prop(operator
, "use_scene_unit")
333 layrow
.label(text
="", icon
='EMPTY_ARROWS' if operator
.use_scene_unit
else 'EMPTY_DATA')
334 layout
.prop(operator
, "axis_forward")
335 layout
.prop(operator
, "axis_up")
339 def menu_func_export(self
, context
):
340 self
.layout
.operator(Export3DS
.bl_idname
, text
="3D Studio (.3ds)")
343 def menu_func_import(self
, context
):
344 self
.layout
.operator(Import3DS
.bl_idname
, text
="3D Studio (.3ds)")
348 bpy
.utils
.register_class(Import3DS
)
349 bpy
.utils
.register_class(MAX3DS_PT_import_include
)
350 bpy
.utils
.register_class(MAX3DS_PT_import_transform
)
351 bpy
.utils
.register_class(Export3DS
)
352 bpy
.utils
.register_class(MAX3DS_PT_export_include
)
353 bpy
.utils
.register_class(MAX3DS_PT_export_transform
)
354 bpy
.types
.TOPBAR_MT_file_import
.append(menu_func_import
)
355 bpy
.types
.TOPBAR_MT_file_export
.append(menu_func_export
)
359 bpy
.utils
.unregister_class(Import3DS
)
360 bpy
.utils
.unregister_class(MAX3DS_PT_import_include
)
361 bpy
.utils
.unregister_class(MAX3DS_PT_import_transform
)
362 bpy
.utils
.unregister_class(Export3DS
)
363 bpy
.utils
.unregister_class(MAX3DS_PT_export_include
)
364 bpy
.utils
.unregister_class(MAX3DS_PT_export_transform
)
365 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_func_import
)
366 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_func_export
)
369 if __name__
== "__main__":