Blender V5.0
stl_export.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#include <memory>
10
11#include "BKE_context.hh"
12#include "BKE_lib_id.hh"
13#include "BKE_mesh_wrapper.hh"
14#include "BKE_object.hh"
15#include "BKE_report.hh"
16#include "BKE_scene.hh"
17
18#include "BLI_string.h"
19#include "BLI_string_utils.hh"
20
22
23#include "DNA_mesh_types.h"
24#include "DNA_scene_types.h"
25
26#include "BLI_math_matrix.h"
27#include "BLI_math_rotation.h"
28#include "BLI_math_vector.hh"
30
31#include "IO_stl.hh"
32
33#include "stl_data.hh"
34#include "stl_export.hh"
35#include "stl_export_writer.hh"
36
37#include "CLG_log.h"
38static CLG_LogRef LOG = {"io.stl"};
39
41
42void export_frame(Depsgraph *depsgraph,
43 float scene_unit_scale,
44 const STLExportParams &export_params)
45{
46 std::unique_ptr<FileWriter> writer;
47
48 /* If not exporting in batch, create single writer for all objects. */
49 if (!export_params.use_batch) {
50 try {
51 writer = std::make_unique<FileWriter>(export_params.filepath, export_params.ascii_format);
52 }
53 catch (const std::runtime_error &ex) {
54 CLOG_ERROR(&LOG, "Error: %s", ex.what());
55 BKE_reportf(export_params.reports,
57 "STL Export: Cannot open file '%s'",
58 export_params.filepath);
59 return;
60 }
61 }
62
63 DEGObjectIterSettings deg_iter_settings{};
64 deg_iter_settings.depsgraph = depsgraph;
68
69 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, object) {
70 if (object->type != OB_MESH) {
71 continue;
72 }
73
74 if (export_params.export_selected_objects && !(object->base_flag & BASE_SELECTED)) {
75 continue;
76 }
77
78 /* If exporting in batch, create writer for each iteration over objects. */
79 if (export_params.use_batch) {
80 /* Get object name by skipping initial "OB" prefix. */
81 char object_name[sizeof(object->id.name) - 2];
82 STRNCPY(object_name, object->id.name + 2);
83 BLI_path_make_safe_filename(object_name);
84 /* Replace spaces with underscores. */
85 BLI_string_replace_char(object_name, ' ', '_');
86
87 /* Include object name in the exported file name. */
88 char filepath[FILE_MAX];
89 STRNCPY(filepath, export_params.filepath);
90 /* When basename is just ".stl", regular path functions would
91 * treat it as a hidden file called ".stl". Remove the extension
92 * before trying to add a suffix. */
93 const char *basename = BLI_path_basename(filepath);
94 if (basename != nullptr && BLI_strcasecmp(basename, ".stl") == 0) {
95 *const_cast<char *>(basename) = '\0';
96 }
97
98 BLI_path_suffix(filepath, FILE_MAX, object_name, "");
99 /* Make sure we have `.stl` extension (case insensitive). */
100 if (!BLI_path_extension_check(filepath, ".stl")) {
101 BLI_path_extension_ensure(filepath, FILE_MAX, ".stl");
102 }
103
104 try {
105 writer = std::make_unique<FileWriter>(filepath, export_params.ascii_format);
106 }
107 catch (const std::runtime_error &ex) {
108 CLOG_ERROR(&LOG, "Error: %s", ex.what());
110 export_params.reports, RPT_ERROR, "STL Export: Cannot open file '%s'", filepath);
111 return;
112 }
113 }
114
115 Object *obj_eval = DEG_get_evaluated(depsgraph, object);
116 const Mesh *mesh = export_params.apply_modifiers ? BKE_object_get_evaluated_mesh(obj_eval) :
118
119 /* Ensure data exists if currently in edit mode. */
120 BKE_mesh_wrapper_ensure_mdata(const_cast<Mesh *>(mesh));
121
122 /* Calculate transform. */
123 float global_scale = export_params.global_scale * scene_unit_scale;
124 float axes_transform[3][3];
125 unit_m3(axes_transform);
126 float xform[4][4];
127 /* +Y-forward and +Z-up are the default Blender axis settings. */
129 export_params.forward_axis, export_params.up_axis, IO_AXIS_Y, IO_AXIS_Z, axes_transform);
130 mul_m4_m3m4(xform, axes_transform, obj_eval->object_to_world().ptr());
131 /* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */
132 mul_v3_m3v3(xform[3], axes_transform, obj_eval->object_to_world().location());
133 xform[3][3] = obj_eval->object_to_world()[3][3];
134
135 const bool mirrored = is_negative_m4(xform);
136
137 /* Write triangles. */
138 const Span<float3> positions = mesh->vert_positions();
139 const Span<int> corner_verts = mesh->corner_verts();
140 for (const int3 &tri : mesh->corner_tris()) {
142 for (int i = 0; i < 3; i++) {
143 /* Reverse face order for mirrored objects. */
144 int idx = mirrored ? 2 - i : i;
145 float3 pos = positions[corner_verts[tri[idx]]];
146 mul_m4_v3(xform, pos);
147 pos *= global_scale;
148 data.vertices[i] = pos;
149 }
150 data.normal = math::normal_tri(data.vertices[0], data.vertices[1], data.vertices[2]);
151 writer->write_triangle(data);
152 }
153 }
155}
156
157void exporter_main(const bContext *C, const STLExportParams &export_params)
158{
159 Depsgraph *depsgraph = nullptr;
160 bool needs_free = false;
161
162 Main *bmain = CTX_data_main(C);
163 Scene *scene = CTX_data_scene(C);
164 if (export_params.collection[0]) {
165 Collection *collection = reinterpret_cast<Collection *>(
166 BKE_libblock_find_name(bmain, ID_GR, export_params.collection));
167 if (!collection) {
168 BKE_reportf(export_params.reports,
169 RPT_ERROR,
170 "STL Export: Unable to find collection '%s'",
171 export_params.collection);
172 return;
173 }
174
175 ViewLayer *view_layer = CTX_data_view_layer(C);
176
177 depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
178 needs_free = true;
181 }
182 else {
184 }
185
186 float scene_unit_scale = 1.0f;
187 if ((scene->unit.system != USER_UNIT_NONE) && export_params.use_scene_unit) {
188 scene_unit_scale = scene->unit.scale_length;
189 }
190
191 export_frame(depsgraph, scene_unit_scale, export_params);
192
193 if (needs_free) {
195 }
196}
197
198} // namespace blender::io::stl
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
ID * BKE_libblock_find_name(Main *bmain, short type, const char *name, const std::optional< Library * > lib=std::nullopt) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition lib_id.cc:1710
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_pre_modified_mesh(const Object *object)
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
void unit_m3(float m[3][3])
void mul_m4_v3(const float M[4][4], float r[3])
bool is_negative_m4(const float mat[4][4])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4])
bool mat3_from_axis_conversion(int src_forward, int src_up, int dst_forward, int dst_up, float r_mat[3][3])
bool bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const char *sep) ATTR_NONNULL(1
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#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
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
int char char int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
void BLI_string_replace_char(char *str, char src, char dst) ATTR_NONNULL(1)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
@ DAG_EVAL_RENDER
Depsgraph * DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
Definition depsgraph.cc:278
void DEG_graph_free(Depsgraph *graph)
Definition depsgraph.cc:306
void DEG_graph_build_from_collection(Depsgraph *graph, Collection *collection)
#define DEG_OBJECT_ITER_BEGIN(settings_, instance_)
#define DEG_OBJECT_ITER_END
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_VISIBLE
@ DEG_ITER_OBJECT_FLAG_DUPLI
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
@ ID_GR
@ OB_MESH
#define BASE_SELECTED(v3d, base)
@ USER_UNIT_NONE
@ IO_AXIS_Y
@ IO_AXIS_Z
#define C
Definition RandGen.cpp:29
BMesh const char void * data
BPy_StructRNA * depsgraph
uint pos
#define LOG(level)
Definition log.h:97
void export_frame(Depsgraph *depsgraph, float scene_unit_scale, const STLExportParams &export_params)
Definition stl_export.cc:42
void exporter_main(const bContext *C, const STLExportParams &export_params)
VecBase< T, 3 > normal_tri(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3)
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
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
struct UnitSettings unit
i
Definition text_draw.cc:230