Blender  V3.3
obj_exporter.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <cstdio>
8 #include <exception>
9 #include <memory>
10 
11 #include "BKE_scene.h"
12 
13 #include "BLI_path_util.h"
14 #include "BLI_task.hh"
15 #include "BLI_vector.hh"
16 
17 #include "DEG_depsgraph_query.h"
18 
19 #include "DNA_scene_types.h"
20 
21 #include "ED_object.h"
22 
23 #include "obj_export_mesh.hh"
24 #include "obj_export_nurbs.hh"
25 #include "obj_exporter.hh"
26 
28 
29 namespace blender::io::obj {
30 
32 {
34  Main *bmain = CTX_data_main(C);
35  ViewLayer *view_layer = CTX_data_view_layer(C);
36  if (eval_mode == DAG_EVAL_RENDER) {
37  depsgraph_ = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
38  needs_free_ = true;
40  BKE_scene_graph_evaluated_ensure(depsgraph_, bmain);
41  }
42  else {
44  needs_free_ = false;
45  }
46 }
47 
49 {
50  if (needs_free_) {
51  DEG_graph_free(depsgraph_);
52  }
53 }
54 
56 {
57  return depsgraph_;
58 }
59 
61 {
63 }
64 
65 static void print_exception_error(const std::system_error &ex)
66 {
67  std::cerr << ex.code().category().name() << ": " << ex.what() << ": " << ex.code().message()
68  << std::endl;
69 }
70 
71 static bool is_curve_nurbs_compatible(const Nurb *nurb)
72 {
73  while (nurb) {
74  if (nurb->type == CU_BEZIER || nurb->pntsv != 1) {
75  return false;
76  }
77  nurb = nurb->next;
78  }
79  return true;
80 }
81 
87 std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>>
89 {
90  Vector<std::unique_ptr<OBJMesh>> r_exportable_meshes;
91  Vector<std::unique_ptr<OBJCurve>> r_exportable_nurbs;
92  const int deg_objects_visibility_flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
96  DEG_OBJECT_ITER_BEGIN (depsgraph, object, deg_objects_visibility_flags) {
97  if (export_params.export_selected_objects && !(object->base_flag & BASE_SELECTED)) {
98  continue;
99  }
100  switch (object->type) {
101  case OB_SURF:
102  /* Evaluated surface objects appear as mesh objects from the iterator. */
103  break;
104  case OB_MESH:
105  r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object));
106  break;
107  case OB_CURVES_LEGACY: {
108  Curve *curve = static_cast<Curve *>(object->data);
109  Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)};
110  if (!nurb) {
111  /* An empty curve. Not yet supported to export these as meshes. */
112  if (export_params.export_curves_as_nurbs) {
113  r_exportable_nurbs.append(
114  std::make_unique<OBJCurve>(depsgraph, export_params, object));
115  }
116  break;
117  }
118  if (export_params.export_curves_as_nurbs && is_curve_nurbs_compatible(nurb)) {
119  /* Export in parameter form: control points. */
120  r_exportable_nurbs.append(std::make_unique<OBJCurve>(depsgraph, export_params, object));
121  }
122  else {
123  /* Export in mesh form: edges and vertices. */
124  r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object));
125  }
126  break;
127  }
128  default:
129  /* Other object types are not supported. */
130  break;
131  }
132  }
134  return {std::move(r_exportable_meshes), std::move(r_exportable_nurbs)};
135 }
136 
137 static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_mesh,
138  OBJWriter &obj_writer,
139  MTLWriter *mtl_writer,
140  const OBJExportParams &export_params)
141 {
142  /* Parallelization is over meshes/objects, which means
143  * we have to have the output text buffer for each object,
144  * and write them all into the file at the end. */
145  size_t count = exportable_as_mesh.size();
146  std::vector<FormatHandler<eFileType::OBJ>> buffers(count);
147 
148  /* Serial: gather material indices, ensure normals & edges. */
149  Vector<Vector<int>> mtlindices;
150  if (mtl_writer) {
151  obj_writer.write_mtllib_name(mtl_writer->mtl_file_path());
152  mtlindices.reserve(count);
153  }
154  for (auto &obj_mesh : exportable_as_mesh) {
155  OBJMesh &obj = *obj_mesh;
156  if (mtl_writer) {
157  mtlindices.append(mtl_writer->add_materials(obj));
158  }
159  if (export_params.export_normals) {
160  obj.ensure_mesh_normals();
161  }
162  obj.ensure_mesh_edges();
163  }
164 
165  /* Parallel over meshes: store normal coords & indices, uv coords and indices. */
167  for (const int i : range) {
168  OBJMesh &obj = *exportable_as_mesh[i];
169  if (export_params.export_normals) {
170  obj.store_normal_coords_and_indices();
171  }
172  if (export_params.export_uv) {
173  obj.store_uv_coords_and_indices();
174  }
175  }
176  });
177 
178  /* Serial: calculate index offsets; these are sequentially added
179  * over all meshes, and requite normal/uv indices to be calculated. */
180  Vector<IndexOffsets> index_offsets;
181  index_offsets.reserve(count);
182  IndexOffsets offsets{0, 0, 0};
183  for (auto &obj_mesh : exportable_as_mesh) {
184  OBJMesh &obj = *obj_mesh;
185  index_offsets.append(offsets);
186  offsets.vertex_offset += obj.tot_vertices();
187  offsets.uv_vertex_offset += obj.tot_uv_vertices();
188  offsets.normal_offset += obj.tot_normal_indices();
189  }
190 
191  /* Parallel over meshes: main result writing. */
193  for (const int i : range) {
194  OBJMesh &obj = *exportable_as_mesh[i];
195  auto &fh = buffers[i];
196 
197  obj_writer.write_object_name(fh, obj);
198  obj_writer.write_vertex_coords(fh, obj, export_params.export_colors);
199 
200  if (obj.tot_polygons() > 0) {
201  if (export_params.export_smooth_groups) {
202  obj.calc_smooth_groups(export_params.smooth_groups_bitflags);
203  }
204  if (export_params.export_materials) {
205  obj.calc_poly_order();
206  }
207  if (export_params.export_normals) {
208  obj_writer.write_poly_normals(fh, obj);
209  }
210  if (export_params.export_uv) {
211  obj_writer.write_uv_coords(fh, obj);
212  }
213  /* This function takes a 0-indexed slot index for the obj_mesh object and
214  * returns the material name that we are using in the .obj file for it. */
215  const auto *obj_mtlindices = mtlindices.is_empty() ? nullptr : &mtlindices[i];
216  std::function<const char *(int)> matname_fn = [&](int s) -> const char * {
217  if (!obj_mtlindices || s < 0 || s >= obj_mtlindices->size()) {
218  return nullptr;
219  }
220  return mtl_writer->mtlmaterial_name((*obj_mtlindices)[s]);
221  };
222  obj_writer.write_poly_elements(fh, index_offsets[i], obj, matname_fn);
223  }
224  obj_writer.write_edges_indices(fh, index_offsets[i], obj);
225 
226  /* Nothing will need this object's data after this point, release
227  * various arrays here. */
228  obj.clear();
229  }
230  });
231 
232  /* Write all the object text buffers into the output file. */
233  FILE *f = obj_writer.get_outfile();
234  for (auto &b : buffers) {
235  b.write_to_file(f);
236  }
237 }
238 
242 static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
243  const OBJWriter &obj_writer)
244 {
246  /* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
247  * to wait for #blender::Vector to clean the objects up. */
248  for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
249  obj_writer.write_nurbs_curve(fh, *obj_curve);
250  }
251  fh.write_to_file(obj_writer.get_outfile());
252 }
253 
254 void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
255 {
256  std::unique_ptr<OBJWriter> frame_writer = nullptr;
257  try {
258  frame_writer = std::make_unique<OBJWriter>(filepath, export_params);
259  }
260  catch (const std::system_error &ex) {
262  return;
263  }
264  if (!frame_writer) {
265  BLI_assert(!"File should be writable by now.");
266  return;
267  }
268  std::unique_ptr<MTLWriter> mtl_writer = nullptr;
269  if (export_params.export_materials) {
270  try {
271  mtl_writer = std::make_unique<MTLWriter>(filepath);
272  }
273  catch (const std::system_error &ex) {
275  }
276  }
277 
278  frame_writer->write_header();
279 
280  auto [exportable_as_mesh, exportable_as_nurbs] = filter_supported_objects(depsgraph,
281  export_params);
282 
284  std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params);
285  if (mtl_writer) {
286  mtl_writer->write_header(export_params.blen_filepath);
287  char dest_dir[PATH_MAX];
288  if (export_params.file_base_for_tests[0] == '\0') {
289  BLI_split_dir_part(export_params.filepath, dest_dir, PATH_MAX);
290  }
291  else {
292  BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX);
293  }
294  BLI_path_slash_native(dest_dir);
295  BLI_path_normalize(nullptr, dest_dir);
296  mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir);
297  }
298  write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
299 }
300 
301 bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames)
302 {
303  BLI_strncpy(r_filepath_with_frames, filepath, FILE_MAX);
304  BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, "");
305  const int digits = frame == 0 ? 1 : integer_digits_i(abs(frame));
306  BLI_path_frame(r_filepath_with_frames, frame, digits);
307  return BLI_path_extension_replace(r_filepath_with_frames, FILE_MAX, ".obj");
308 }
309 
310 void exporter_main(bContext *C, const OBJExportParams &export_params)
311 {
313  OBJDepsgraph obj_depsgraph(C, export_params.export_eval_mode);
314  Scene *scene = DEG_get_input_scene(obj_depsgraph.get());
315  const char *filepath = export_params.filepath;
316 
317  /* Single frame export, i.e. no animation. */
318  if (!export_params.export_animation) {
319  fprintf(stderr, "Writing to %s\n", filepath);
320  export_frame(obj_depsgraph.get(), export_params, filepath);
321  return;
322  }
323 
324  char filepath_with_frames[FILE_MAX];
325  /* Used to reset the Scene to its original state. */
326  const int original_frame = scene->r.cfra;
327 
328  for (int frame = export_params.start_frame; frame <= export_params.end_frame; frame++) {
329  const bool filepath_ok = append_frame_to_filename(filepath, frame, filepath_with_frames);
330  if (!filepath_ok) {
331  fprintf(stderr, "Error: File Path too long.\n%s\n", filepath_with_frames);
332  return;
333  }
334 
335  scene->r.cfra = frame;
336  obj_depsgraph.update_for_newframe();
337  fprintf(stderr, "Writing to %s\n", filepath_with_frames);
338  export_frame(obj_depsgraph.get(), export_params, filepath_with_frames);
339  }
340  scene->r.cfra = original_frame;
341 }
342 } // namespace blender::io::obj
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1528
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.cc:2728
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain)
Definition: scene.cc:2653
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define PATH_MAX
Definition: BLI_fileops.h:29
MINLINE int integer_digits_i(int i)
void BLI_split_dir_part(const char *string, char *dir, size_t dirlen)
Definition: path_util.c:1490
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL()
Definition: path_util.c:709
#define FILE_MAX
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:131
bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext) ATTR_NONNULL()
Definition: path_util.c:1393
void BLI_path_slash_native(char *path) ATTR_NONNULL()
Definition: path_util.c:1805
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
Depsgraph * DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, eEvaluationMode mode)
Definition: depsgraph.cc:267
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
eEvaluationMode
Definition: DEG_depsgraph.h:44
@ DAG_EVAL_RENDER
Definition: DEG_depsgraph.h:46
void DEG_graph_free(Depsgraph *graph)
Definition: depsgraph.cc:295
void DEG_graph_build_for_all_objects(struct Depsgraph *graph)
struct Scene * DEG_get_input_scene(const Depsgraph *graph)
#define DEG_OBJECT_ITER_END
#define DEG_OBJECT_ITER_BEGIN(graph_, instance_, flag_)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_VISIBLE
@ DEG_ITER_OBJECT_FLAG_DUPLI
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
@ CU_BEZIER
@ BASE_SELECTED
@ OB_MODE_OBJECT
@ OB_SURF
@ OB_MESH
@ OB_CURVES_LEGACY
bool ED_object_mode_set(struct bContext *C, eObjectMode mode)
Definition: object_modes.c:231
#define C
Definition: RandGen.cpp:25
void append(const T &value)
Definition: BLI_vector.hh:433
bool is_empty() const
Definition: BLI_vector.hh:706
void reserve(const int64_t min_capacity)
Definition: BLI_vector.hh:340
Vector< int > add_materials(const OBJMesh &mesh_to_export)
const char * mtlmaterial_name(int index)
OBJDepsgraph(const bContext *C, eEvaluationMode eval_mode)
Definition: obj_exporter.cc:31
void write_nurbs_curve(FormatHandler< eFileType::OBJ > &fh, const OBJCurve &obj_nurbs_data) const
void write_poly_elements(FormatHandler< eFileType::OBJ > &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data, std::function< const char *(int)> matname_fn)
void write_mtllib_name(const StringRefNull mtl_filepath) const
void write_vertex_coords(FormatHandler< eFileType::OBJ > &fh, const OBJMesh &obj_mesh_data, bool write_colors) const
void write_edges_indices(FormatHandler< eFileType::OBJ > &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data) const
void write_object_name(FormatHandler< eFileType::OBJ > &fh, const OBJMesh &obj_mesh_data) const
Scene scene
Curve curve
const Depsgraph * depsgraph
int count
static void print_exception_error(const std::system_error &ex)
Definition: obj_exporter.cc:65
static void write_mesh_objects(Vector< std::unique_ptr< OBJMesh >> exportable_as_mesh, OBJWriter &obj_writer, MTLWriter *mtl_writer, const OBJExportParams &export_params)
static bool is_curve_nurbs_compatible(const Nurb *nurb)
Definition: obj_exporter.cc:71
bool append_frame_to_filename(const char *filepath, const int frame, char *r_filepath_with_frames)
std::pair< Vector< std::unique_ptr< OBJMesh > >, Vector< std::unique_ptr< OBJCurve > > > filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params)
Definition: obj_exporter.cc:88
void exporter_main(bContext *C, const OBJExportParams &export_params)
void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
static void write_nurbs_curve_objects(const Vector< std::unique_ptr< OBJCurve >> &exportable_as_nurbs, const OBJWriter &obj_writer)
T abs(const T &a)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
ListBase nurb
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
struct Nurb * next
short type
eEvaluationMode export_eval_mode
const char * blen_filepath
char file_base_for_tests[FILE_MAX]
ePathReferenceMode path_mode
char filepath[FILE_MAX]
struct RenderData r