Merge branch 'blender-v4.0-release'
[blender-addons.git] / object_print3d_utils / export.py
blobd8c5412fe6dcec06b01187f95ecbe87f328bba47
1 # SPDX-FileCopyrightText: 2013-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Export wrappers and integration with external tools.
8 import bpy
10 from bpy.app.translations import (
11 pgettext_tip as tip_,
12 pgettext_data as data_,
16 def image_get(mat):
17 from bpy_extras import node_shader_utils
19 if mat.use_nodes:
20 mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat)
21 base_color_tex = mat_wrap.base_color_texture
22 if base_color_tex and base_color_tex.image:
23 return base_color_tex.image
26 def image_copy_guess(filepath, objects):
27 # 'filepath' is the path we are writing to.
28 image = None
29 mats = set()
31 for obj in objects:
32 for slot in obj.material_slots:
33 if slot.material:
34 mats.add(slot.material)
36 for mat in mats:
37 image = image_get(mat)
38 if image is not None:
39 break
41 if image is not None:
42 import os
43 import shutil
45 imagepath = bpy.path.abspath(image.filepath, library=image.library)
46 if os.path.exists(imagepath):
47 filepath_noext = os.path.splitext(filepath)[0]
48 ext = os.path.splitext(imagepath)[1]
50 imagepath_dst = filepath_noext + ext
51 print(f"copying texture: {imagepath!r} -> {imagepath_dst!r}")
53 try:
54 shutil.copy(imagepath, imagepath_dst)
55 except:
56 import traceback
57 traceback.print_exc()
60 def write_mesh(context, report_cb):
61 import os
63 scene = context.scene
64 layer = context.view_layer
65 unit = scene.unit_settings
66 print_3d = scene.print_3d
68 export_format = print_3d.export_format
69 global_scale = unit.scale_length if (unit.system != 'NONE' and print_3d.use_apply_scale) else 1.0
70 path_mode = 'COPY' if print_3d.use_export_texture else 'AUTO'
71 export_path = bpy.path.abspath(print_3d.export_path)
72 obj = layer.objects.active
73 export_data_layers = print_3d.use_data_layers
75 # Create name 'export_path/blendname-objname'
76 # add the filename component
77 if bpy.data.is_saved:
78 name = os.path.basename(bpy.data.filepath)
79 name = os.path.splitext(name)[0]
80 else:
81 name = data_("untitled")
83 # add object name
84 import re
85 name += "-" + re.sub(r'[\\/:*?"<>|]', "", obj.name)
87 # first ensure the path is created
88 if export_path:
89 # this can fail with strange errors,
90 # if the dir can't be made then we get an error later.
91 try:
92 os.makedirs(export_path, exist_ok=True)
93 except:
94 import traceback
95 traceback.print_exc()
97 filepath = os.path.join(export_path, name)
99 # ensure addon is enabled
100 import addon_utils
102 def addon_ensure(addon_id):
103 # Enable the addon, dont change preferences.
104 _default_state, loaded_state = addon_utils.check(addon_id)
105 if not loaded_state:
106 addon_utils.enable(addon_id, default_set=False)
108 if export_format == 'STL':
109 addon_ensure("io_mesh_stl")
110 filepath = bpy.path.ensure_ext(filepath, ".stl")
111 ret = bpy.ops.export_mesh.stl(
112 filepath=filepath,
113 ascii=False,
114 use_mesh_modifiers=True,
115 use_selection=True,
116 global_scale=global_scale,
118 elif export_format == 'PLY':
119 filepath = bpy.path.ensure_ext(filepath, ".ply")
120 ret = bpy.ops.wm.ply_export(
121 filepath=filepath,
122 ascii_format=False,
123 apply_modifiers=True,
124 export_selected_objects=True,
125 global_scale=global_scale,
126 export_normals=export_data_layers,
127 export_uv=export_data_layers,
128 export_colors="SRGB" if export_data_layers else "NONE",
130 elif export_format == 'X3D':
131 addon_ensure("io_scene_x3d")
132 filepath = bpy.path.ensure_ext(filepath, ".x3d")
133 ret = bpy.ops.export_scene.x3d(
134 filepath=filepath,
135 use_mesh_modifiers=True,
136 use_selection=True,
137 global_scale=global_scale,
138 path_mode=path_mode,
139 use_normals=export_data_layers,
141 elif export_format == 'OBJ':
142 filepath = bpy.path.ensure_ext(filepath, ".obj")
143 ret = bpy.ops.wm.obj_export(
144 filepath=filepath,
145 apply_modifiers=True,
146 export_selected_objects=True,
147 global_scale=global_scale,
148 path_mode=path_mode,
149 export_normals=export_data_layers,
150 export_uv=export_data_layers,
151 export_materials=export_data_layers,
152 export_colors=export_data_layers,
154 else:
155 assert 0
157 # for formats that don't support images
158 if path_mode == 'COPY' and export_format in {'STL', 'PLY'}:
159 image_copy_guess(filepath, context.selected_objects)
161 if 'FINISHED' in ret:
162 if report_cb is not None:
163 report_cb({'INFO'}, tip_("Exported: {!r}").format(filepath))
165 return True
167 if report_cb is not None:
168 report_cb({'ERROR'}, "Export failed")
170 return False