Blender  V3.3
obj_export_file_writer.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <algorithm>
8 #include <cstdio>
9 
10 #include "BKE_blender_version.h"
11 #include "BKE_geometry_set.hh"
12 
13 #include "BLI_color.hh"
15 #include "BLI_path_util.h"
16 #include "BLI_task.hh"
17 
18 #include "IO_path_util.hh"
19 
20 #include "obj_export_mesh.hh"
21 #include "obj_export_mtl.hh"
22 #include "obj_export_nurbs.hh"
23 
25 
26 namespace blender::io::obj {
34 const int SMOOTH_GROUP_DISABLED = 0;
35 const int SMOOTH_GROUP_DEFAULT = 1;
36 
37 static const char *DEFORM_GROUP_DISABLED = "off";
38 /* There is no deform group default name. Use what the user set in the UI. */
39 
45 static const char *MATERIAL_GROUP_DISABLED = "";
46 
47 void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
48  const IndexOffsets &offsets,
49  Span<int> vert_indices,
50  Span<int> uv_indices,
51  Span<int> normal_indices,
52  bool flip) const
53 {
54  BLI_assert(vert_indices.size() == uv_indices.size() &&
55  vert_indices.size() == normal_indices.size());
56  const int vertex_offset = offsets.vertex_offset + 1;
57  const int uv_offset = offsets.uv_vertex_offset + 1;
58  const int normal_offset = offsets.normal_offset + 1;
59  const int n = vert_indices.size();
61  if (!flip) {
62  for (int j = 0; j < n; ++j) {
63  fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
64  uv_indices[j] + uv_offset,
65  normal_indices[j] + normal_offset);
66  }
67  }
68  else {
69  /* For a transform that is mirrored (negative scale on odd number of axes),
70  * we want to flip the face index order. Start from the same index, and
71  * then go backwards. Same logic in other write_*_indices functions below. */
72  for (int k = 0; k < n; ++k) {
73  int j = k == 0 ? 0 : n - k;
74  fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
75  uv_indices[j] + uv_offset,
76  normal_indices[j] + normal_offset);
77  }
78  }
80 }
81 
82 void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
83  const IndexOffsets &offsets,
84  Span<int> vert_indices,
85  Span<int> /*uv_indices*/,
86  Span<int> normal_indices,
87  bool flip) const
88 {
89  BLI_assert(vert_indices.size() == normal_indices.size());
90  const int vertex_offset = offsets.vertex_offset + 1;
91  const int normal_offset = offsets.normal_offset + 1;
92  const int n = vert_indices.size();
94  if (!flip) {
95  for (int j = 0; j < n; ++j) {
96  fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
97  normal_indices[j] + normal_offset);
98  }
99  }
100  else {
101  for (int k = 0; k < n; ++k) {
102  int j = k == 0 ? 0 : n - k;
103  fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
104  normal_indices[j] + normal_offset);
105  }
106  }
108 }
109 
110 void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
111  const IndexOffsets &offsets,
112  Span<int> vert_indices,
113  Span<int> uv_indices,
114  Span<int> /*normal_indices*/,
115  bool flip) const
116 {
117  BLI_assert(vert_indices.size() == uv_indices.size());
118  const int vertex_offset = offsets.vertex_offset + 1;
119  const int uv_offset = offsets.uv_vertex_offset + 1;
120  const int n = vert_indices.size();
122  if (!flip) {
123  for (int j = 0; j < n; ++j) {
124  fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
125  uv_indices[j] + uv_offset);
126  }
127  }
128  else {
129  for (int k = 0; k < n; ++k) {
130  int j = k == 0 ? 0 : n - k;
131  fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
132  uv_indices[j] + uv_offset);
133  }
134  }
136 }
137 
138 void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
139  const IndexOffsets &offsets,
140  Span<int> vert_indices,
141  Span<int> /*uv_indices*/,
142  Span<int> /*normal_indices*/,
143  bool flip) const
144 {
145  const int vertex_offset = offsets.vertex_offset + 1;
146  const int n = vert_indices.size();
148  if (!flip) {
149  for (int j = 0; j < n; ++j) {
150  fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
151  }
152  }
153  else {
154  for (int k = 0; k < n; ++k) {
155  int j = k == 0 ? 0 : n - k;
156  fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
157  }
158  }
160 }
161 
163 {
164  using namespace std::string_literals;
166  fh.write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + "\n");
167  fh.write<eOBJSyntaxElement::string>("# www.blender.org\n");
168  fh.write_to_file(outfile_);
169 }
170 
171 void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
172 {
173  /* Split .MTL file path into parent directory and filename. */
174  char mtl_file_name[FILE_MAXFILE];
175  char mtl_dir_name[FILE_MAXDIR];
176  BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
178  fh.write<eOBJSyntaxElement::mtllib>(mtl_file_name);
179  fh.write_to_file(outfile_);
180 }
181 
182 static void spaces_to_underscores(std::string &r_name)
183 {
184  std::replace(r_name.begin(), r_name.end(), ' ', '_');
185 }
186 
188  const OBJMesh &obj_mesh_data) const
189 {
190  std::string object_name = obj_mesh_data.get_object_name();
192  if (export_params_.export_object_groups) {
193  std::string mesh_name = obj_mesh_data.get_object_mesh_name();
194  spaces_to_underscores(mesh_name);
195  fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mesh_name);
196  return;
197  }
199 }
200 
201 /* Split up large meshes into multi-threaded jobs; each job processes
202  * this amount of items. */
203 static const int chunk_size = 32768;
204 static int calc_chunk_count(int count)
205 {
206  return (count + chunk_size - 1) / chunk_size;
207 }
208 
209 /* Write /tot_count/ items to OBJ file output. Each item is written
210  * by a /function/ that should be independent from other items.
211  * If the amount of items is large enough (> chunk_size), then writing
212  * will be done in parallel, into temporary FormatHandler buffers that
213  * will be written into the final /fh/ buffer at the end.
214  */
215 template<typename Function>
217  int tot_count,
218  const Function &function)
219 {
220  if (tot_count <= 0) {
221  return;
222  }
223  /* If we have just one chunk, process it directly into the output
224  * buffer - avoids all the job scheduling and temporary vector allocation
225  * overhead. */
226  const int chunk_count = calc_chunk_count(tot_count);
227  if (chunk_count == 1) {
228  for (int i = 0; i < tot_count; i++) {
229  function(fh, i);
230  }
231  return;
232  }
233  /* Give each chunk its own temporary output buffer, and process them in parallel. */
234  std::vector<FormatHandler<eFileType::OBJ>> buffers(chunk_count);
235  blender::threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) {
236  for (const int r : range) {
237  int i_start = r * chunk_size;
238  int i_end = std::min(i_start + chunk_size, tot_count);
239  auto &buf = buffers[r];
240  for (int i = i_start; i < i_end; i++) {
241  function(buf, i);
242  }
243  }
244  });
245  /* Emit all temporary output buffers into the destination buffer. */
246  for (auto &buf : buffers) {
247  fh.append_from(buf);
248  }
249 }
250 
252  const OBJMesh &obj_mesh_data,
253  bool write_colors) const
254 {
255  const int tot_count = obj_mesh_data.tot_vertices();
256 
257  Mesh *mesh = obj_mesh_data.get_mesh();
258  CustomDataLayer *colors_layer = nullptr;
259  if (write_colors) {
260  colors_layer = BKE_id_attributes_active_color_get(&mesh->id);
261  }
262  if (write_colors && (colors_layer != nullptr)) {
263  const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
265  colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f});
266 
267  BLI_assert(tot_count == attribute.size());
268  obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
269  float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
270  ColorGeometry4f linear = attribute.get(i);
271  float srgb[3];
272  linearrgb_to_srgb_v3_v3(srgb, linear);
274  vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
275  });
276  }
277  else {
278  obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
279  float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
280  buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
281  });
282  }
283 }
284 
286 {
287  const Vector<float2> &uv_coords = r_obj_mesh_data.get_uv_coords();
288  const int tot_count = uv_coords.size();
289  obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
290  const float2 &uv_vertex = uv_coords[i];
291  buf.write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
292  });
293 }
294 
296 {
297  /* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
298  const Vector<float3> &normal_coords = obj_mesh_data.get_normal_coords();
299  const int tot_count = normal_coords.size();
300  obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
301  const float3 &normal = normal_coords[i];
303  });
304 }
305 
306 OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
307  const int total_uv_vertices) const
308 {
309  if (export_params_.export_normals) {
310  if (export_params_.export_uv && (total_uv_vertices > 0)) {
311  /* Write both normals and UV indices. */
312  return &OBJWriter::write_vert_uv_normal_indices;
313  }
314  /* Write normals indices. */
315  return &OBJWriter::write_vert_normal_indices;
316  }
317  /* Write UV indices. */
318  if (export_params_.export_uv && (total_uv_vertices > 0)) {
319  return &OBJWriter::write_vert_uv_indices;
320  }
321  /* Write neither normals nor UV indices. */
322  return &OBJWriter::write_vert_indices;
323 }
324 
325 static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params, int poly_idx)
326 {
327  if (poly_idx < 0) {
328  return NEGATIVE_INIT;
329  }
330  int group = SMOOTH_GROUP_DISABLED;
331  if (mesh.is_ith_poly_smooth(poly_idx)) {
332  group = !params.export_smooth_groups ? SMOOTH_GROUP_DEFAULT : mesh.ith_smooth_group(poly_idx);
333  }
334  return group;
335 }
336 
338  const IndexOffsets &offsets,
339  const OBJMesh &obj_mesh_data,
340  std::function<const char *(int)> matname_fn)
341 {
342  const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
343  obj_mesh_data.tot_uv_vertices());
344 
345  const int tot_polygons = obj_mesh_data.tot_polygons();
346  const int tot_deform_groups = obj_mesh_data.tot_deform_groups();
348 
349  obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) {
350  /* Polygon order for writing into the file is not necessarily the same
351  * as order in the mesh; it will be sorted by material indices. Remap current
352  * and previous indices here according to the order. */
353  int prev_i = obj_mesh_data.remap_poly_index(idx - 1);
354  int i = obj_mesh_data.remap_poly_index(idx);
355 
356  Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i);
357  Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i);
358  Vector<int> poly_normal_indices = obj_mesh_data.calc_poly_normal_indices(i);
359 
360  /* Write smoothing group if different from previous. */
361  {
362  const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i);
363  const int group = get_smooth_group(obj_mesh_data, export_params_, i);
364  if (group != prev_group) {
366  }
367  }
368 
369  /* Write vertex group if different from previous. */
370  if (export_params_.export_vertex_groups) {
371  Vector<float> &local_weights = group_weights.local();
372  local_weights.resize(tot_deform_groups);
373  const int16_t prev_group = idx == 0 ? NEGATIVE_INIT :
374  obj_mesh_data.get_poly_deform_group_index(
375  prev_i, local_weights);
376  const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights);
377  if (group != prev_group) {
379  group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
380  obj_mesh_data.get_poly_deform_group_name(group));
381  }
382  }
383 
384  /* Write material name and material group if different from previous. */
385  if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) {
386  const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(prev_i);
387  const int16_t mat = obj_mesh_data.ith_poly_matnr(i);
388  if (mat != prev_mat) {
389  if (mat == NOT_FOUND) {
391  }
392  else {
393  const char *mat_name = matname_fn(mat);
394  if (!mat_name) {
395  mat_name = MATERIAL_GROUP_DISABLED;
396  }
397  if (export_params_.export_material_groups) {
398  std::string object_name = obj_mesh_data.get_object_name();
400  fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name);
401  }
402  buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name);
403  }
404  }
405  }
406 
407  /* Write polygon elements. */
408  (this->*poly_element_writer)(buf,
409  offsets,
410  poly_vertex_indices,
411  poly_uv_indices,
412  poly_normal_indices,
413  obj_mesh_data.is_mirrored_transform());
414  });
415 }
416 
418  const IndexOffsets &offsets,
419  const OBJMesh &obj_mesh_data) const
420 {
421  /* NOTE: ensure_mesh_edges should be called before. */
422  const int tot_edges = obj_mesh_data.tot_edges();
423  for (int edge_index = 0; edge_index < tot_edges; edge_index++) {
424  const std::optional<std::array<int, 2>> vertex_indices =
425  obj_mesh_data.calc_loose_edge_vert_indices(edge_index);
426  if (!vertex_indices) {
427  continue;
428  }
429  fh.write<eOBJSyntaxElement::edge>((*vertex_indices)[0] + offsets.vertex_offset + 1,
430  (*vertex_indices)[1] + offsets.vertex_offset + 1);
431  }
432 }
433 
435  const OBJCurve &obj_nurbs_data) const
436 {
437  const int total_splines = obj_nurbs_data.total_splines();
438  for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
439  const int total_vertices = obj_nurbs_data.total_spline_vertices(spline_idx);
440  for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
441  const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
442  spline_idx, vertex_idx, export_params_.scaling_factor);
445  }
446 
447  const char *nurbs_name = obj_nurbs_data.get_curve_name();
448  const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
449  fh.write<eOBJSyntaxElement::object_group>(nurbs_name);
459  const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
461  for (int i = 0; i < total_control_points; i++) {
462  /* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
463  * last vertex coordinate, -2 second last. */
464  fh.write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
465  }
467 
474  const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx);
475  const bool cyclic = flagsu & CU_NURB_CYCLIC;
476  const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT);
478  for (int i = 1; i <= total_control_points + 2; i++) {
479  float parm = 1.0f * i / (total_control_points + 2 + 1);
480  if (endpoint) {
481  if (i <= nurbs_degree) {
482  parm = 0;
483  }
484  else if (i > total_control_points + 2 - nurbs_degree) {
485  parm = 1;
486  }
487  }
489  }
491 
493  }
494 }
495 
496 /* -------------------------------------------------------------------- */
504 static std::string float3_to_string(const float3 &numbers)
505 {
506  std::ostringstream r_string;
507  r_string << numbers[0] << " " << numbers[1] << " " << numbers[2];
508  return r_string.str();
509 };
510 
511 MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false)
512 {
513  mtl_filepath_ = obj_filepath;
514  const bool ok = BLI_path_extension_replace(mtl_filepath_.data(), FILE_MAX, ".mtl");
515  if (!ok) {
516  throw std::system_error(ENAMETOOLONG, std::system_category(), "");
517  }
518  outfile_ = BLI_fopen(mtl_filepath_.c_str(), "wb");
519  if (!outfile_) {
520  throw std::system_error(errno, std::system_category(), "Cannot open file " + mtl_filepath_);
521  }
522 }
524 {
525  if (outfile_) {
526  fmt_handler_.write_to_file(outfile_);
527  if (std::fclose(outfile_)) {
528  std::cerr << "Error: could not close the file '" << mtl_filepath_
529  << "' properly, it may be corrupted." << std::endl;
530  }
531  }
532 }
533 
534 void MTLWriter::write_header(const char *blen_filepath)
535 {
536  using namespace std::string_literals;
537  const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
538  BLI_path_basename(blen_filepath) :
539  "None";
540  fmt_handler_.write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
541  " MTL File: '" + blen_basename + "'\n");
542  fmt_handler_.write<eMTLSyntaxElement::string>("# www.blender.org\n");
543 }
544 
546 {
547  return mtl_filepath_;
548 }
549 
550 void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl)
551 {
552  /* For various material properties, we only capture information
553  * coming from the texture, or the default value of the socket.
554  * When the texture is present, do not emit the default value. */
556  fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl.Ns);
557  }
558  fmt_handler_.write<eMTLSyntaxElement::Ka>(mtl.Ka.x, mtl.Ka.y, mtl.Ka.z);
560  fmt_handler_.write<eMTLSyntaxElement::Kd>(mtl.Kd.x, mtl.Kd.y, mtl.Kd.z);
561  }
563  fmt_handler_.write<eMTLSyntaxElement::Ks>(mtl.Ks.x, mtl.Ks.y, mtl.Ks.z);
564  }
566  fmt_handler_.write<eMTLSyntaxElement::Ke>(mtl.Ke.x, mtl.Ke.y, mtl.Ke.z);
567  }
568  fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl.Ni);
570  fmt_handler_.write<eMTLSyntaxElement::d>(mtl.d);
571  }
572  fmt_handler_.write<eMTLSyntaxElement::illum>(mtl.illum);
573 }
574 
575 void MTLWriter::write_texture_map(
576  const MTLMaterial &mtl_material,
577  const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map,
578  const char *blen_filedir,
579  const char *dest_dir,
580  ePathReferenceMode path_mode,
581  Set<std::pair<std::string, std::string>> &copy_set)
582 {
583  std::string options;
584  /* Option strings should have their own leading spaces. */
585  if (texture_map.value.translation != float3{0.0f, 0.0f, 0.0f}) {
586  options.append(" -o ").append(float3_to_string(texture_map.value.translation));
587  }
588  if (texture_map.value.scale != float3{1.0f, 1.0f, 1.0f}) {
589  options.append(" -s ").append(float3_to_string(texture_map.value.scale));
590  }
591  if (texture_map.key == eMTLSyntaxElement::map_Bump && mtl_material.map_Bump_strength > 0.0001f) {
592  options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
593  }
594 
595  std::string path = path_reference(
596  texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set);
597  /* Always emit forward slashes for cross-platform compatibility. */
598  std::replace(path.begin(), path.end(), '\\', '/');
599 
600 #define SYNTAX_DISPATCH(eMTLSyntaxElement) \
601  if (texture_map.key == eMTLSyntaxElement) { \
602  fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \
603  return; \
604  }
605 
613 #undef SYNTAX_DISPATCH
614 
615  BLI_assert(!"This map type was not written to the file.");
616 }
617 
618 void MTLWriter::write_materials(const char *blen_filepath,
619  ePathReferenceMode path_mode,
620  const char *dest_dir)
621 {
622  if (mtlmaterials_.size() == 0) {
623  return;
624  }
625 
626  char blen_filedir[PATH_MAX];
627  BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX);
628  BLI_path_slash_native(blen_filedir);
629  BLI_path_normalize(nullptr, blen_filedir);
630 
631  std::sort(mtlmaterials_.begin(),
632  mtlmaterials_.end(),
633  [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
635  for (const MTLMaterial &mtlmat : mtlmaterials_) {
636  fmt_handler_.write<eMTLSyntaxElement::string>("\n");
637  fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name);
638  write_bsdf_properties(mtlmat);
639  for (const auto &tex : mtlmat.texture_maps.items()) {
640  if (!tex.value.is_valid()) {
641  continue;
642  }
643  write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set);
644  }
645  }
646  path_reference_copy(copy_set);
647 }
648 
650 {
651  Vector<int> r_mtl_indices;
652  r_mtl_indices.resize(mesh_to_export.tot_materials());
653  for (int16_t i = 0; i < mesh_to_export.tot_materials(); i++) {
654  const Material *material = mesh_to_export.get_object_material(i);
655  if (!material) {
656  r_mtl_indices[i] = -1;
657  continue;
658  }
659  int mtlmat_index = material_map_.lookup_default(material, -1);
660  if (mtlmat_index != -1) {
661  r_mtl_indices[i] = mtlmat_index;
662  }
663  else {
664  mtlmaterials_.append(mtlmaterial_for_material(material));
665  r_mtl_indices[i] = mtlmaterials_.size() - 1;
666  material_map_.add_new(material, r_mtl_indices[i]);
667  }
668  }
669  return r_mtl_indices;
670 }
671 
672 const char *MTLWriter::mtlmaterial_name(int index)
673 {
674  if (index < 0 || index >= mtlmaterials_.size()) {
675  return nullptr;
676  }
677  return mtlmaterials_[index].name.c_str();
678 }
681 } // namespace blender::io::obj
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
struct CustomDataLayer * BKE_id_attributes_active_color_get(const struct ID *id)
const char * BKE_blender_version_string(void)
Definition: blender.c:124
#define BLI_assert(a)
Definition: BLI_assert.h:46
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:906
#define PATH_MAX
Definition: BLI_fileops.h:29
MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
void BLI_split_dir_part(const char *string, char *dir, size_t dirlen)
Definition: path_util.c:1490
const char * BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1653
#define FILE_MAXFILE
void BLI_split_dirfile(const char *string, char *dir, char *file, size_t dirlen, size_t filelen)
Definition: path_util.c:1465
#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
#define FILE_MAXDIR
@ CU_NURB_CYCLIC
@ CU_NURB_ENDPOINT
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
ePathReferenceMode
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr const char * data() const
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
void resize(const int64_t new_size)
Definition: BLI_vector.hh:353
GVArray lookup_or_default(const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type, const void *default_value=nullptr) const
constexpr void write(T &&...args)
void append_from(FormatHandler< filetype, buffer_chunk_size > &v)
MTLWriter(const char *obj_filepath) noexcept(false)
Vector< int > add_materials(const OBJMesh &mesh_to_export)
void write_header(const char *blen_filepath)
void write_materials(const char *blen_filepath, ePathReferenceMode path_mode, const char *dest_dir)
const char * mtlmaterial_name(int index)
int total_spline_vertices(int spline_index) const
int total_spline_control_points(int spline_index) const
short get_nurbs_flagu(int spline_index) const
int get_nurbs_degree(int spline_index) const
const char * get_curve_name() const
float3 vertex_coordinates(int spline_index, int vertex_index, float scaling_factor) const
const char * get_object_mesh_name() const
const char * get_object_name() const
const Vector< float2 > & get_uv_coords() const
Span< int > calc_poly_uv_indices(int poly_index) const
const char * get_poly_deform_group_name(int16_t def_group_index) const
float3 calc_vertex_coords(int vert_index, float scaling_factor) const
const Material * get_object_material(int16_t mat_nr) const
int16_t get_poly_deform_group_index(int poly_index, MutableSpan< float > group_weights) const
int16_t ith_poly_matnr(int poly_index) const
const Vector< float3 > & get_normal_coords() const
int remap_poly_index(int i) const
Vector< int > calc_poly_vertex_indices(int poly_index) const
std::optional< std::array< int, 2 > > calc_loose_edge_vert_indices(int edge_index) const
Vector< int > calc_poly_normal_indices(int poly_index) const
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_uv_coords(FormatHandler< eFileType::OBJ > &fh, OBJMesh &obj_mesh_data) 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
void write_poly_normals(FormatHandler< eFileType::OBJ > &fh, OBJMesh &obj_mesh_data)
CCL_NAMESPACE_BEGIN struct Options options
Material material
IconTextureDrawCall normal
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
static unsigned a[3]
Definition: RandGen.cpp:78
AttributeAccessor mesh_attributes(const Mesh &mesh)
static void spaces_to_underscores(std::string &r_name)
static const char * MATERIAL_GROUP_DISABLED
MTLMaterial mtlmaterial_for_material(const Material *material)
const int NEGATIVE_INIT
static std::string float3_to_string(const float3 &numbers)
static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params, int poly_idx)
static const int chunk_size
static int calc_chunk_count(int count)
void obj_parallel_chunked_output(FormatHandler< eFileType::OBJ > &fh, int tot_count, const Function &function)
static const char * DEFORM_GROUP_DISABLED
void path_reference_copy(const Set< std::pair< std::string, std::string >> &copy_set)
Definition: path_util.cc:59
std::string path_reference(StringRefNull filepath, StringRefNull base_src, StringRefNull base_dst, ePathReferenceMode mode, Set< std::pair< std::string, std::string >> *copy_set)
Definition: path_util.cc:9
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
std::string to_string(const T &n)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define SYNTAX_DISPATCH(eMTLSyntaxElement)
#define min(a, b)
Definition: sort.c:35
signed short int16_t
Definition: stdint.h:76
float size
Definition: particles.h:27
const tex_map_XX & tex_map_of_type(const eMTLSyntaxElement key) const