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