Blender V5.0
io_stl_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#ifdef WITH_IO_STL
10
11# include "BKE_context.hh"
12# include "BKE_file_handler.hh"
13# include "BKE_report.hh"
14
15# include "BLI_string.h"
16# include "BLI_string_utf8.h"
17
18# include "WM_api.hh"
19# include "WM_types.hh"
20
21# include "DNA_space_types.h"
22
23# include "ED_fileselect.hh"
24# include "ED_outliner.hh"
25
26# include "RNA_access.hh"
27# include "RNA_define.hh"
28
29# include "BLT_translation.hh"
30
31# include "UI_interface.hh"
32# include "UI_interface_layout.hh"
33# include "UI_resources.hh"
34
35# include "IO_stl.hh"
36# include "io_stl_ops.hh"
37# include "io_utils.hh"
38
39static wmOperatorStatus wm_stl_export_invoke(bContext *C,
40 wmOperator *op,
41 const wmEvent * /*event*/)
42{
44
47}
48
49static wmOperatorStatus wm_stl_export_exec(bContext *C, wmOperator *op)
50{
51 if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
52 BKE_report(op->reports, RPT_ERROR, "No filename given");
53 return OPERATOR_CANCELLED;
54 }
55 STLExportParams export_params;
56 RNA_string_get(op->ptr, "filepath", export_params.filepath);
57 export_params.forward_axis = eIOAxis(RNA_enum_get(op->ptr, "forward_axis"));
58 export_params.up_axis = eIOAxis(RNA_enum_get(op->ptr, "up_axis"));
59 export_params.global_scale = RNA_float_get(op->ptr, "global_scale");
60 export_params.apply_modifiers = RNA_boolean_get(op->ptr, "apply_modifiers");
61 export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects");
62 export_params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit");
63 export_params.ascii_format = RNA_boolean_get(op->ptr, "ascii_format");
64 export_params.use_batch = RNA_boolean_get(op->ptr, "use_batch");
65
66 RNA_string_get(op->ptr, "collection", export_params.collection);
67
68 export_params.reports = op->reports;
69
70 STL_export(C, &export_params);
71
73 return OPERATOR_CANCELLED;
74 }
75
76 BKE_report(op->reports, RPT_INFO, "File exported successfully");
77 return OPERATOR_FINISHED;
78}
79
80static void wm_stl_export_draw(bContext *C, wmOperator *op)
81{
82 uiLayout *layout = op->layout;
83 PointerRNA *ptr = op->ptr;
84
85 layout->use_property_split_set(true);
86 layout->use_property_decorate_set(false);
87
88 if (uiLayout *panel = layout->panel(C, "STL_export_general", false, IFACE_("General"))) {
89 uiLayout *col = &panel->column(false);
90
91 uiLayout *sub = &col->column(false, IFACE_("Format"));
92 sub->prop(ptr, "ascii_format", UI_ITEM_NONE, IFACE_("ASCII"), ICON_NONE);
93
94 /* The Batch mode and Selection only options only make sense when using regular export. */
95 if (CTX_wm_space_file(C)) {
96 col->prop(ptr, "use_batch", UI_ITEM_NONE, IFACE_("Batch"), ICON_NONE);
97
98 sub = &col->column(false, IFACE_("Include"));
99 sub->prop(ptr, "export_selected_objects", UI_ITEM_NONE, IFACE_("Selection Only"), ICON_NONE);
100 }
101
102 sub->prop(ptr, "global_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
103 sub->prop(ptr, "use_scene_unit", UI_ITEM_NONE, IFACE_("Scene Unit"), ICON_NONE);
104 sub->prop(ptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward"), ICON_NONE);
105 sub->prop(ptr, "up_axis", UI_ITEM_NONE, IFACE_("Up"), ICON_NONE);
106 }
107
108 if (uiLayout *panel = layout->panel(C, "STL_export_geometry", false, IFACE_("Geometry"))) {
109 uiLayout *col = &panel->column(false);
110 col->prop(ptr, "apply_modifiers", UI_ITEM_NONE, IFACE_("Apply Modifiers"), ICON_NONE);
111 }
112}
113
117static bool wm_stl_export_check(bContext * /*C*/, wmOperator *op)
118{
119 char filepath[FILE_MAX];
120 bool changed = false;
121 bool use_batch = RNA_boolean_get(op->ptr, "use_batch");
122 RNA_string_get(op->ptr, "filepath", filepath);
123
124 /* Enforce an extension on the filepath unless Batch mode is used. Batch mode
125 * will perform substitutions, including the extension, during its processing. */
126 if (!use_batch && !BLI_path_extension_check(filepath, ".stl")) {
127 BLI_path_extension_ensure(filepath, FILE_MAX, ".stl");
128 RNA_string_set(op->ptr, "filepath", filepath);
129 changed = true;
130 }
131 return changed;
132}
133
135{
136 PropertyRNA *prop;
137
138 ot->name = "Export STL";
139 ot->description = "Save the scene to an STL file";
140 ot->idname = "WM_OT_stl_export";
141
142 ot->invoke = wm_stl_export_invoke;
143 ot->exec = wm_stl_export_exec;
145 ot->ui = wm_stl_export_draw;
146 ot->check = wm_stl_export_check;
147
148 ot->flag = OPTYPE_PRESET;
149
153 FILE_SAVE,
157
158 RNA_def_boolean(ot->srna,
159 "ascii_format",
160 false,
161 "ASCII Format",
162 "Export file in ASCII format, export as binary otherwise");
164 ot->srna, "use_batch", false, "Batch Export", "Export each object to a separate file");
165 RNA_def_boolean(ot->srna,
166 "export_selected_objects",
167 false,
168 "Export Selected Objects",
169 "Export only selected objects instead of all supported objects");
170
171 prop = RNA_def_string(ot->srna,
172 "collection",
173 nullptr,
174 MAX_ID_NAME - 2,
175 "Source Collection",
176 "Export only objects from this collection (and its children)");
178
179 RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
180 RNA_def_boolean(ot->srna,
181 "use_scene_unit",
182 false,
183 "Scene Unit",
184 "Apply current scene's unit (as defined by unit scale) to exported data");
185
186 prop = RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", "");
188
189 prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", "");
191
193 ot->srna, "apply_modifiers", true, "Apply Modifiers", "Apply modifiers to exported meshes");
194
195 /* Only show `.stl` files by default. */
196 prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
198}
199
200static wmOperatorStatus wm_stl_import_exec(bContext *C, wmOperator *op)
201{
203 params.forward_axis = eIOAxis(RNA_enum_get(op->ptr, "forward_axis"));
204 params.up_axis = eIOAxis(RNA_enum_get(op->ptr, "up_axis"));
205 params.use_facet_normal = RNA_boolean_get(op->ptr, "use_facet_normal");
206 params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit");
207 params.global_scale = RNA_float_get(op->ptr, "global_scale");
208 params.use_mesh_validate = RNA_boolean_get(op->ptr, "use_mesh_validate");
209
210 params.reports = op->reports;
211
213
214 if (paths.is_empty()) {
215 BKE_report(op->reports, RPT_ERROR, "No filepath given");
216 return OPERATOR_CANCELLED;
217 }
218 for (const auto &path : paths) {
219 STRNCPY(params.filepath, path.c_str());
221 }
222
223 Scene *scene = CTX_data_scene(C);
228
229 return OPERATOR_FINISHED;
230}
231
232static bool wm_stl_import_check(bContext * /*C*/, wmOperator *op)
233{
234 const int num_axes = 3;
235 /* Both forward and up axes cannot be the same (or same except opposite sign). */
236 if (RNA_enum_get(op->ptr, "forward_axis") % num_axes ==
237 (RNA_enum_get(op->ptr, "up_axis") % num_axes))
238 {
239 RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % num_axes + 1);
240 return true;
241 }
242 return false;
243}
244
245static void ui_stl_import_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr)
246{
247 layout->use_property_split_set(true);
248 layout->use_property_decorate_set(false);
249
250 if (uiLayout *panel = layout->panel(C, "STL_import_general", false, IFACE_("General"))) {
251 uiLayout *col = &panel->column(false);
252 col->prop(ptr, "global_scale", UI_ITEM_NONE, std::nullopt, ICON_NONE);
253 col->prop(ptr, "use_scene_unit", UI_ITEM_NONE, std::nullopt, ICON_NONE);
254 col->prop(ptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward Axis"), ICON_NONE);
255 col->prop(ptr, "up_axis", UI_ITEM_NONE, std::nullopt, ICON_NONE);
256 }
257
258 if (uiLayout *panel = layout->panel(C, "STL_import_options", false, IFACE_("Options"))) {
259 uiLayout *col = &panel->column(false);
260 col->prop(ptr, "use_facet_normal", UI_ITEM_NONE, std::nullopt, ICON_NONE);
261 col->prop(ptr, "use_mesh_validate", UI_ITEM_NONE, std::nullopt, ICON_NONE);
262 }
263}
264
265static void wm_stl_import_draw(bContext *C, wmOperator *op)
266{
267 ui_stl_import_settings(C, op->layout, op->ptr);
268}
269
271{
272 PropertyRNA *prop;
273
274 ot->name = "Import STL";
275 ot->description = "Import an STL file as an object";
276 ot->idname = "WM_OT_stl_import";
277
279 ot->exec = wm_stl_import_exec;
281 ot->check = wm_stl_import_check;
282 ot->ui = wm_stl_import_draw;
283 ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
284
293
294 RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
295 RNA_def_boolean(ot->srna,
296 "use_scene_unit",
297 false,
298 "Scene Unit",
299 "Apply current scene's unit (as defined by unit scale) to imported data");
300 RNA_def_boolean(ot->srna,
301 "use_facet_normal",
302 false,
303 "Facet Normals",
304 "Use (import) facet normals (note that this will still give flat shading)");
305 RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", "");
306 RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", "");
307
309 ot->srna,
310 "use_mesh_validate",
311 true,
312 "Validate Mesh",
313 "Ensure the data is valid "
314 "(when disabled, data may be imported which causes crashes displaying or editing)");
315
316 /* Only show `.stl` files by default. */
317 prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
319}
320
321namespace blender::ed::io {
323{
324 auto fh = std::make_unique<blender::bke::FileHandlerType>();
325 STRNCPY_UTF8(fh->idname, "IO_FH_stl");
326 STRNCPY_UTF8(fh->import_operator, "WM_OT_stl_import");
327 STRNCPY_UTF8(fh->export_operator, "WM_OT_stl_export");
328 STRNCPY_UTF8(fh->label, "STL");
329 STRNCPY_UTF8(fh->file_extensions_str, ".stl");
330 fh->poll_drop = poll_file_object_drop;
331 bke::file_handler_add(std::move(fh));
332}
333} // namespace blender::ed::io
334
335#endif /* WITH_IO_STL */
SpaceFile * CTX_wm_space_file(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
bool BKE_reports_contain(ReportList *reports, eReportType level)
Definition report.cc:383
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define FILE_MAX
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define STRNCPY_UTF8(dst, src)
#define IFACE_(msgid)
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ FILE_SORT_DEFAULT
@ FILE_BLENDER
@ FILE_TYPE_FOLDER
@ FILE_DEFAULTDISPLAY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
void ED_fileselect_ensure_default_filepath(bContext *C, wmOperator *op, const char *extension)
Definition filesel.cc:1470
void ED_outliner_select_sync_from_object_tag(bContext *C)
eIOAxis
@ IO_AXIS_Y
@ IO_AXIS_Z
void STL_import(bContext *C, const STLImportParams *import_params)
Definition IO_stl.cc:15
void STL_export(bContext *C, const STLExportParams *export_params)
Definition IO_stl.cc:21
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
@ WM_FILESEL_FILES
Definition WM_api.hh:1125
@ WM_FILESEL_DIRECTORY
Definition WM_api.hh:1122
@ WM_FILESEL_SHOW_PROPS
Definition WM_api.hh:1127
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1124
@ FILE_OPENFILE
Definition WM_api.hh:1133
@ FILE_SAVE
Definition WM_api.hh:1134
#define ND_OB_ACTIVE
Definition WM_types.hh:440
#define ND_OB_SELECT
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_PRESET
Definition WM_types.hh:195
@ OPTYPE_UNDO
Definition WM_types.hh:182
#define ND_LAYER_CONTENT
Definition WM_types.hh:453
uint col
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void WM_OT_stl_import(wmOperatorType *ot)
void WM_OT_stl_export(wmOperatorType *ot)
void file_handler_add(std::unique_ptr< FileHandlerType > file_handler)
void stl_file_handler_add()
bool poll_file_object_drop(const bContext *C, blender::bke::FileHandlerType *)
Definition io_utils.cc:58
Vector< std::string > paths_from_operator_properties(PointerRNA *ptr)
Definition io_utils.cc:75
wmOperatorStatus filesel_drop_import_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition io_utils.cc:26
const EnumPropertyItem io_transform_axis[]
void io_ui_forward_axis_update(Main *, Scene *, PointerRNA *ptr)
void io_ui_up_axis_update(Main *, Scene *, PointerRNA *ptr)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool use_ghost)
float RNA_float_get(PointerRNA *ptr, const char *name)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
void RNA_def_property_update_runtime(PropertyRNA *prop, RNAPropertyUpdateFunc func)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
eIOAxis up_axis
Definition IO_stl.hh:38
char collection[MAX_ID_NAME - 2]
Definition IO_stl.hh:45
char filepath[FILE_MAX]
Definition IO_stl.hh:36
bool export_selected_objects
Definition IO_stl.hh:40
bool apply_modifiers
Definition IO_stl.hh:42
eIOAxis forward_axis
Definition IO_stl.hh:37
bool ascii_format
Definition IO_stl.hh:43
ReportList * reports
Definition IO_stl.hh:47
float global_scale
Definition IO_stl.hh:39
bool use_scene_unit
Definition IO_stl.hh:41
void use_property_decorate_set(bool is_sep)
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
bool WM_operator_winactive(bContext *C)