Blender  V3.3
obj_export_io.hh
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #pragma once
8 
9 #include <cstdio>
10 #include <string>
11 #include <type_traits>
12 #include <vector>
13 
14 #include "BLI_compiler_attrs.h"
15 #include "BLI_fileops.h"
16 #include "BLI_string_ref.hh"
17 #include "BLI_utility_mixins.hh"
18 
19 /* SEP macro from BLI path utils clashes with SEP symbol in fmt headers. */
20 #undef SEP
21 #define FMT_HEADER_ONLY
22 #include <fmt/format.h>
23 
24 namespace blender::io::obj {
25 
26 enum class eFileType {
27  OBJ,
28  MTL,
29 };
30 
31 enum class eOBJSyntaxElement {
35  normal,
43  edge,
44  cstype,
52  new_line,
53  mtllib,
57  /* Use rarely. New line is NOT included for string. */
58  string,
59 };
60 
61 enum class eMTLSyntaxElement {
62  newmtl,
63  Ni,
64  d,
65  Ns,
66  illum,
67  Ka,
68  Kd,
69  Ks,
70  Ke,
71  map_Kd,
72  map_Ks,
73  map_Ns,
74  map_d,
75  map_refl,
76  map_Ke,
77  map_Bump,
78  /* Use rarely. New line is NOT included for string. */
79  string,
80 };
81 
82 template<eFileType filetype> struct FileTypeTraits;
83 
84 /* Used to prevent mixing of say OBJ file format with MTL syntax elements. */
85 template<> struct FileTypeTraits<eFileType::OBJ> {
87 };
88 
89 template<> struct FileTypeTraits<eFileType::MTL> {
91 };
92 
94  /* Formatting syntax with the file format key like `newmtl %s\n`. */
95  const char *fmt = nullptr;
96  /* Number of arguments needed by the syntax. */
97  const int total_args = 0;
98  /* Whether types of the given arguments are accepted by the syntax above. Fail to compile by
99  * default.
100  */
101  const bool are_types_valid = false;
102 };
103 
107 template<typename T> struct always_false : std::false_type {
108 };
109 
110 template<typename... T>
111 constexpr bool is_type_float = (... && std::is_floating_point_v<std::decay_t<T>>);
112 
113 template<typename... T>
114 constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>);
115 
116 template<typename... T>
117 constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>);
118 
119 /* GCC (at least 9.3) while compiling the obj_exporter_tests.cc with optimizations on,
120  * results in "obj_export_io.hh:205:18: warning: ā€˜%s’ directive output truncated writing 34 bytes
121  * into a region of size 6" and similar warnings. Yes the output is truncated, and that is covered
122  * as an edge case by tests on purpose. */
123 #if defined(__GNUC__) && !defined(__clang__)
124 # pragma GCC diagnostic push
125 # pragma GCC diagnostic ignored "-Wformat-truncation"
126 #endif
127 template<typename... T>
129 {
130  switch (key) {
132  return {"v {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
133  }
135  return {"v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", 6, is_type_float<T...>};
136  }
138  return {"vt {:.6f} {:.6f}\n", 2, is_type_float<T...>};
139  }
141  return {"vn {:.4f} {:.4f} {:.4f}\n", 3, is_type_float<T...>};
142  }
144  return {"f", 0, is_type_string_related<T...>};
145  }
147  return {" {}/{}/{}", 3, is_type_integral<T...>};
148  }
150  return {" {}//{}", 2, is_type_integral<T...>};
151  }
153  return {" {}/{}", 2, is_type_integral<T...>};
154  }
156  return {" {}", 1, is_type_integral<T...>};
157  }
159  return {"usemtl {}\n", 1, is_type_string_related<T...>};
160  }
162  return {"l {} {}\n", 2, is_type_integral<T...>};
163  }
165  return {"cstype bspline\n", 0, is_type_string_related<T...>};
166  }
168  return {"deg {}\n", 1, is_type_integral<T...>};
169  }
171  return {"curv 0.0 1.0", 0, is_type_string_related<T...>};
172  }
174  return {"parm u 0.0", 0, is_type_string_related<T...>};
175  }
177  return {" {:.6f}", 1, is_type_float<T...>};
178  }
180  return {" 1.0\n", 0, is_type_string_related<T...>};
181  }
183  return {"end\n", 0, is_type_string_related<T...>};
184  }
187  }
190  }
192  return {"\n", 0, is_type_string_related<T...>};
193  }
195  return {"mtllib {}\n", 1, is_type_string_related<T...>};
196  }
198  return {"s {}\n", 1, is_type_integral<T...>};
199  }
201  return {"g {}\n", 1, is_type_string_related<T...>};
202  }
204  return {"o {}\n", 1, is_type_string_related<T...>};
205  }
207  return {"{}", 1, is_type_string_related<T...>};
208  }
209  }
210 }
211 
212 template<typename... T>
214 {
215  switch (key) {
217  return {"newmtl {}\n", 1, is_type_string_related<T...>};
218  }
219  case eMTLSyntaxElement::Ni: {
220  return {"Ni {:.6f}\n", 1, is_type_float<T...>};
221  }
222  case eMTLSyntaxElement::d: {
223  return {"d {:.6f}\n", 1, is_type_float<T...>};
224  }
225  case eMTLSyntaxElement::Ns: {
226  return {"Ns {:.6f}\n", 1, is_type_float<T...>};
227  }
229  return {"illum {}\n", 1, is_type_integral<T...>};
230  }
231  case eMTLSyntaxElement::Ka: {
232  return {"Ka {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
233  }
234  case eMTLSyntaxElement::Kd: {
235  return {"Kd {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
236  }
237  case eMTLSyntaxElement::Ks: {
238  return {"Ks {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
239  }
240  case eMTLSyntaxElement::Ke: {
241  return {"Ke {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
242  }
243  /* NOTE: first texture map related argument, if present, will have its own leading space. */
245  return {"map_Kd{} {}\n", 2, is_type_string_related<T...>};
246  }
248  return {"map_Ks{} {}\n", 2, is_type_string_related<T...>};
249  }
251  return {"map_Ns{} {}\n", 2, is_type_string_related<T...>};
252  }
254  return {"map_d{} {}\n", 2, is_type_string_related<T...>};
255  }
257  return {"map_refl{} {}\n", 2, is_type_string_related<T...>};
258  }
260  return {"map_Ke{} {}\n", 2, is_type_string_related<T...>};
261  }
263  return {"map_Bump{} {}\n", 2, is_type_string_related<T...>};
264  }
266  return {"{}", 1, is_type_string_related<T...>};
267  }
268  }
269 }
270 #if defined(__GNUC__) && !defined(__clang__)
271 # pragma GCC diagnostic pop
272 #endif
273 
281 template<eFileType filetype, size_t buffer_chunk_size = 64 * 1024>
283  private:
284  typedef std::vector<char> VectorChar;
285  std::vector<VectorChar> blocks_;
286 
287  public:
288  /* Write contents to the buffer(s) into a file, and clear the buffers. */
289  void write_to_file(FILE *f)
290  {
291  for (const auto &b : blocks_)
292  fwrite(b.data(), 1, b.size(), f);
293  blocks_.clear();
294  }
295 
296  std::string get_as_string() const
297  {
298  std::string s;
299  for (const auto &b : blocks_)
300  s.append(b.data(), b.size());
301  return s;
302  }
303  size_t get_block_count() const
304  {
305  return blocks_.size();
306  }
307 
309  {
310  blocks_.insert(blocks_.end(),
311  std::make_move_iterator(v.blocks_.begin()),
312  std::make_move_iterator(v.blocks_.end()));
313  v.blocks_.clear();
314  }
315 
322  template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
323  constexpr void write(T &&...args)
324  {
325  /* Get format syntax, number of arguments expected and whether types of given arguments are
326  * valid.
327  */
328  constexpr FormattingSyntax fmt_nargs_valid = syntax_elem_to_formatting<T...>(key);
329  BLI_STATIC_ASSERT(fmt_nargs_valid.are_types_valid &&
330  (sizeof...(T) == fmt_nargs_valid.total_args),
331  "Types of all arguments and the number of arguments should match what the "
332  "formatting specifies.");
333  write_impl(fmt_nargs_valid.fmt, std::forward<T>(args)...);
334  }
335 
336  private:
337  /* Ensure the last block contains at least this amount of free space.
338  * If not, add a new block with max of block size & the amount of space needed. */
339  void ensure_space(size_t at_least)
340  {
341  if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
342  VectorChar &b = blocks_.emplace_back(VectorChar());
343  b.reserve(std::max(at_least, buffer_chunk_size));
344  }
345  }
346 
347  template<typename... T> void write_impl(const char *fmt, T &&...args)
348  {
349  /* Format into a local buffer. */
350  fmt::memory_buffer buf;
351  fmt::format_to(fmt::appender(buf), fmt, std::forward<T>(args)...);
352  size_t len = buf.size();
353  ensure_space(len);
354  VectorChar &bb = blocks_.back();
355  bb.insert(bb.end(), buf.begin(), buf.end());
356  }
357 };
358 
359 } // namespace blender::io::obj
#define BLI_STATIC_ASSERT(a, msg)
Definition: BLI_assert.h:83
#define ATTR_FALLTHROUGH
File and directory operations.
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr void write(T &&...args)
std::string get_as_string() const
void append_from(FormatHandler< filetype, buffer_chunk_size > &v)
int len
Definition: draw_manager.c:108
#define T
constexpr bool is_type_string_related
constexpr bool is_type_integral
constexpr bool is_type_float
constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
float max