Blender V4.3
obj_export_file_writer.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 <algorithm>
10#include <sstream>
11
12#include "BKE_attribute.hh"
13#include "BKE_blender_version.h"
14#include "BKE_mesh.hh"
15
16#include "BLI_color.hh"
18#include "BLI_math_matrix.hh"
19#include "BLI_path_utils.hh"
20#include "BLI_string.h"
21#include "BLI_task.hh"
22
23#include "IO_path_util.hh"
24
25#include "obj_export_mesh.hh"
26#include "obj_export_mtl.hh"
27#include "obj_export_nurbs.hh"
28
30
41
42static const char *DEFORM_GROUP_DISABLED = "off";
43/* There is no deform group default name. Use what the user set in the UI. */
44
50static const char *MATERIAL_GROUP_DISABLED = "";
51
52void OBJWriter::write_vert_uv_normal_indices(FormatHandler &fh,
53 const IndexOffsets &offsets,
54 Span<int> vert_indices,
55 Span<int> uv_indices,
56 Span<int> normal_indices,
57 bool flip) const
58{
59 BLI_assert(vert_indices.size() == uv_indices.size() &&
60 vert_indices.size() == normal_indices.size());
61 const int vertex_offset = offsets.vertex_offset + 1;
62 const int uv_offset = offsets.uv_vertex_offset + 1;
63 const int normal_offset = offsets.normal_offset + 1;
64 const int n = vert_indices.size();
66 if (!flip) {
67 for (int j = 0; j < n; ++j) {
68 fh.write_obj_face_v_uv_normal(vert_indices[j] + vertex_offset,
69 uv_indices[j] + uv_offset,
70 normal_indices[j] + normal_offset);
71 }
72 }
73 else {
74 /* For a transform that is mirrored (negative scale on odd number of axes),
75 * we want to flip the face index order. Start from the same index, and
76 * then go backwards. Same logic in other write_*_indices functions below. */
77 for (int k = 0; k < n; ++k) {
78 int j = k == 0 ? 0 : n - k;
79 fh.write_obj_face_v_uv_normal(vert_indices[j] + vertex_offset,
80 uv_indices[j] + uv_offset,
81 normal_indices[j] + normal_offset);
82 }
83 }
85}
86
87void OBJWriter::write_vert_normal_indices(FormatHandler &fh,
88 const IndexOffsets &offsets,
89 Span<int> vert_indices,
90 Span<int> /*uv_indices*/,
91 Span<int> normal_indices,
92 bool flip) const
93{
94 BLI_assert(vert_indices.size() == normal_indices.size());
95 const int vertex_offset = offsets.vertex_offset + 1;
96 const int normal_offset = offsets.normal_offset + 1;
97 const int n = vert_indices.size();
98 fh.write_obj_face_begin();
99 if (!flip) {
100 for (int j = 0; j < n; ++j) {
101 fh.write_obj_face_v_normal(vert_indices[j] + vertex_offset,
102 normal_indices[j] + normal_offset);
103 }
104 }
105 else {
106 for (int k = 0; k < n; ++k) {
107 int j = k == 0 ? 0 : n - k;
108 fh.write_obj_face_v_normal(vert_indices[j] + vertex_offset,
109 normal_indices[j] + normal_offset);
110 }
111 }
112 fh.write_obj_face_end();
113}
114
115void OBJWriter::write_vert_uv_indices(FormatHandler &fh,
116 const IndexOffsets &offsets,
117 Span<int> vert_indices,
118 Span<int> uv_indices,
119 Span<int> /*normal_indices*/,
120 bool flip) const
121{
122 BLI_assert(vert_indices.size() == uv_indices.size());
123 const int vertex_offset = offsets.vertex_offset + 1;
124 const int uv_offset = offsets.uv_vertex_offset + 1;
125 const int n = vert_indices.size();
126 fh.write_obj_face_begin();
127 if (!flip) {
128 for (int j = 0; j < n; ++j) {
129 fh.write_obj_face_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
130 }
131 }
132 else {
133 for (int k = 0; k < n; ++k) {
134 int j = k == 0 ? 0 : n - k;
135 fh.write_obj_face_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
136 }
137 }
138 fh.write_obj_face_end();
139}
140
141void OBJWriter::write_vert_indices(FormatHandler &fh,
142 const IndexOffsets &offsets,
143 Span<int> vert_indices,
144 Span<int> /*uv_indices*/,
145 Span<int> /*normal_indices*/,
146 bool flip) const
147{
148 const int vertex_offset = offsets.vertex_offset + 1;
149 const int n = vert_indices.size();
150 fh.write_obj_face_begin();
151 if (!flip) {
152 for (int j = 0; j < n; ++j) {
153 fh.write_obj_face_v(vert_indices[j] + vertex_offset);
154 }
155 }
156 else {
157 for (int k = 0; k < n; ++k) {
158 int j = k == 0 ? 0 : n - k;
159 fh.write_obj_face_v(vert_indices[j] + vertex_offset);
160 }
161 }
162 fh.write_obj_face_end();
163}
164
166{
167 using namespace std::string_literals;
168 FormatHandler fh;
169 fh.write_string("# Blender "s + BKE_blender_version_string());
170 fh.write_string("# www.blender.org");
171 fh.write_to_file(outfile_);
172}
173
174void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
175{
176 /* Split `.MTL` file path into parent directory and filename. */
177 char mtl_file_name[FILE_MAXFILE];
178 char mtl_dir_name[FILE_MAXDIR];
179 BLI_path_split_dir_file(mtl_filepath.data(),
180 mtl_dir_name,
181 sizeof(mtl_dir_name),
182 mtl_file_name,
183 sizeof(mtl_file_name));
184 FormatHandler fh;
185 fh.write_obj_mtllib(mtl_file_name);
186 fh.write_to_file(outfile_);
187}
188
189static void spaces_to_underscores(std::string &r_name)
190{
191 std::replace(r_name.begin(), r_name.end(), ' ', '_');
192}
193
194void OBJWriter::write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const
195{
196 std::string object_name = obj_mesh_data.get_object_name();
197 spaces_to_underscores(object_name);
198 if (export_params_.export_object_groups) {
199 std::string mesh_name = obj_mesh_data.get_object_mesh_name();
200 spaces_to_underscores(mesh_name);
201 fh.write_obj_group(object_name + "_" + mesh_name);
202 return;
203 }
204 fh.write_obj_object(object_name);
205}
206
207/* Split up large meshes into multi-threaded jobs; each job processes
208 * this amount of items. */
209static const int chunk_size = 32768;
210static int calc_chunk_count(int count)
211{
212 return (count + chunk_size - 1) / chunk_size;
213}
214
215/* Write /tot_count/ items to OBJ file output. Each item is written
216 * by a /function/ that should be independent from other items.
217 * If the amount of items is large enough (> chunk_size), then writing
218 * will be done in parallel, into temporary FormatHandler buffers that
219 * will be written into the final /fh/ buffer at the end.
220 */
221template<typename Function>
222void obj_parallel_chunked_output(FormatHandler &fh, int tot_count, const Function &function)
223{
224 if (tot_count <= 0) {
225 return;
226 }
227 /* If we have just one chunk, process it directly into the output
228 * buffer - avoids all the job scheduling and temporary vector allocation
229 * overhead. */
230 const int chunk_count = calc_chunk_count(tot_count);
231 if (chunk_count == 1) {
232 for (int i = 0; i < tot_count; i++) {
233 function(fh, i);
234 }
235 return;
236 }
237 /* Give each chunk its own temporary output buffer, and process them in parallel. */
238 Array<FormatHandler> buffers(chunk_count);
239 threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) {
240 for (const int r : range) {
241 int i_start = r * chunk_size;
242 int i_end = std::min(i_start + chunk_size, tot_count);
243 auto &buf = buffers[r];
244 for (int i = i_start; i < i_end; i++) {
245 function(buf, i);
246 }
247 }
248 });
249 /* Emit all temporary output buffers into the destination buffer. */
250 for (auto &buf : buffers) {
251 fh.append_from(buf);
252 }
253}
254
256 const OBJMesh &obj_mesh_data,
257 bool write_colors) const
258{
259 const int tot_count = obj_mesh_data.tot_vertices();
260
261 const Mesh *mesh = obj_mesh_data.get_mesh();
262 const StringRef name = mesh->active_color_attribute;
263
264 const float4x4 transform = obj_mesh_data.get_world_axes_transform();
265 const Span<float3> positions = obj_mesh_data.get_mesh()->vert_positions();
266
267 if (write_colors && !name.is_empty()) {
268 const bke::AttributeAccessor attributes = mesh->attributes();
269 const VArray<ColorGeometry4f> attribute = *attributes.lookup_or_default<ColorGeometry4f>(
270 name, bke::AttrDomain::Point, {0.0f, 0.0f, 0.0f, 0.0f});
271
272 BLI_assert(tot_count == attribute.size());
273 obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
274 const float3 vertex = math::transform_point(transform, positions[i]);
275 ColorGeometry4f linear = attribute.get(i);
276 float srgb[3];
277 linearrgb_to_srgb_v3_v3(srgb, linear);
278 buf.write_obj_vertex_color(vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
279 });
280 }
281 else {
282 obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
283 const float3 vertex = math::transform_point(transform, positions[i]);
284 buf.write_obj_vertex(vertex[0], vertex[1], vertex[2]);
285 });
286 }
287}
288
289void OBJWriter::write_uv_coords(FormatHandler &fh, OBJMesh &r_obj_mesh_data) const
290{
291 const Span<float2> uv_coords = r_obj_mesh_data.get_uv_coords();
292 obj_parallel_chunked_output(fh, uv_coords.size(), [&](FormatHandler &buf, int i) {
293 const float2 &uv_vertex = uv_coords[i];
294 buf.write_obj_uv(uv_vertex[0], uv_vertex[1]);
295 });
296}
297
299{
300 /* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
301 const Span<float3> normal_coords = obj_mesh_data.get_normal_coords();
302 obj_parallel_chunked_output(fh, normal_coords.size(), [&](FormatHandler &buf, int i) {
303 const float3 &normal = normal_coords[i];
304 buf.write_obj_normal(normal[0], normal[1], normal[2]);
305 });
306}
307
308OBJWriter::func_vert_uv_normal_indices OBJWriter::get_face_element_writer(
309 const int total_uv_vertices) const
310{
311 if (export_params_.export_normals) {
312 if (export_params_.export_uv && (total_uv_vertices > 0)) {
313 /* Write both normals and UV indices. */
314 return &OBJWriter::write_vert_uv_normal_indices;
315 }
316 /* Write normals indices. */
317 return &OBJWriter::write_vert_normal_indices;
318 }
319 /* Write UV indices. */
320 if (export_params_.export_uv && (total_uv_vertices > 0)) {
321 return &OBJWriter::write_vert_uv_indices;
322 }
323 /* Write neither normals nor UV indices. */
324 return &OBJWriter::write_vert_indices;
325}
326
327static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params, int face_idx)
328{
329 if (face_idx < 0) {
330 return NEGATIVE_INIT;
331 }
332 int group = SMOOTH_GROUP_DISABLED;
333 if (mesh.is_ith_face_smooth(face_idx)) {
334 group = !params.export_smooth_groups ? SMOOTH_GROUP_DEFAULT : mesh.ith_smooth_group(face_idx);
335 }
336 return group;
337}
338
340 const IndexOffsets &offsets,
341 const OBJMesh &obj_mesh_data,
342 FunctionRef<const char *(int)> matname_fn)
343{
344 const func_vert_uv_normal_indices face_element_writer = get_face_element_writer(
345 obj_mesh_data.tot_uv_vertices());
346
347 const int tot_faces = obj_mesh_data.tot_faces();
348 const int tot_deform_groups = obj_mesh_data.tot_deform_groups();
350 const bke::AttributeAccessor attributes = obj_mesh_data.get_mesh()->attributes();
351 const VArray<int> material_indices = *attributes.lookup_or_default<int>(
352 "material_index", bke::AttrDomain::Face, 0);
353
354 obj_parallel_chunked_output(fh, tot_faces, [&](FormatHandler &buf, int idx) {
355 /* Polygon order for writing into the file is not necessarily the same
356 * as order in the mesh; it will be sorted by material indices. Remap current
357 * and previous indices here according to the order. */
358 int prev_i = obj_mesh_data.remap_face_index(idx - 1);
359 int i = obj_mesh_data.remap_face_index(idx);
360
361 const Span<int> face_vertex_indices = obj_mesh_data.calc_face_vert_indices(i);
362 const Span<int> face_uv_indices = obj_mesh_data.get_face_uv_indices(i);
363 const Span<int> face_normal_indices = obj_mesh_data.get_face_normal_indices(i);
364
365 /* Write smoothing group if different from previous. */
366 {
367 const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i);
368 const int group = get_smooth_group(obj_mesh_data, export_params_, i);
369 if (group != prev_group) {
370 buf.write_obj_smooth(group);
371 }
372 }
373
374 /* Write vertex group if different from previous. */
375 if (export_params_.export_vertex_groups) {
376 Vector<float> &local_weights = group_weights.local();
377 local_weights.resize(tot_deform_groups);
378 const int16_t prev_group = idx == 0 ? NEGATIVE_INIT :
379 obj_mesh_data.get_face_deform_group_index(
380 prev_i, local_weights);
381 const int16_t group = obj_mesh_data.get_face_deform_group_index(i, local_weights);
382 if (group != prev_group) {
384 obj_mesh_data.get_face_deform_group_name(group));
385 }
386 }
387
388 /* Write material name and material group if different from previous. */
389 if ((export_params_.export_materials || export_params_.export_material_groups) &&
390 obj_mesh_data.tot_materials() > 0)
391 {
392 const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : std::max(0, material_indices[prev_i]);
393 const int16_t mat = std::max(0, material_indices[i]);
394 if (mat != prev_mat) {
395 if (mat == NOT_FOUND) {
396 if (export_params_.export_materials) {
398 }
399 }
400 else {
401 const char *mat_name = matname_fn(mat);
402 if (!mat_name) {
403 mat_name = MATERIAL_GROUP_DISABLED;
404 }
405 if (export_params_.export_material_groups) {
406 std::string object_name = obj_mesh_data.get_object_name();
407 spaces_to_underscores(object_name);
408 buf.write_obj_group(object_name + "_" + mat_name);
409 }
410 if (export_params_.export_materials) {
411 buf.write_obj_usemtl(mat_name);
412 }
413 }
414 }
415 }
416
417 /* Write face elements. */
418 (this->*face_element_writer)(buf,
419 offsets,
420 face_vertex_indices,
421 face_uv_indices,
422 face_normal_indices,
423 obj_mesh_data.is_mirrored_transform());
424 });
425}
426
428 const IndexOffsets &offsets,
429 const OBJMesh &obj_mesh_data) const
430{
431 const Mesh &mesh = *obj_mesh_data.get_mesh();
432 const bke::LooseEdgeCache &loose_edges = mesh.loose_edges();
433 if (loose_edges.count == 0) {
434 return;
435 }
436
437 const Span<int2> edges = mesh.edges();
438 for (const int64_t i : edges.index_range()) {
439 if (loose_edges.is_loose_bits[i]) {
440 const int2 obj_edge = edges[i] + offsets.vertex_offset + 1;
441 fh.write_obj_edge(obj_edge[0], obj_edge[1]);
442 }
443 }
444}
445
446void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const
447{
448 const int total_splines = obj_nurbs_data.total_splines();
449 for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
450 const int total_vertices = obj_nurbs_data.total_spline_vertices(spline_idx);
451 for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
452 const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
453 spline_idx, vertex_idx, export_params_.global_scale);
454 fh.write_obj_vertex(vertex_coords[0], vertex_coords[1], vertex_coords[2]);
455 }
456
457 const char *nurbs_name = obj_nurbs_data.get_curve_name();
458 const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
459 fh.write_obj_group(nurbs_name);
460 fh.write_obj_cstype();
461 fh.write_obj_nurbs_degree(nurbs_degree);
469 const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
471 for (int i = 0; i < total_control_points; i++) {
472 /* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
473 * last vertex coordinate, -2 second last. */
474 fh.write_obj_face_v(-((i % total_vertices) + 1));
475 }
477
484 const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx);
485 const bool cyclic = flagsu & CU_NURB_CYCLIC;
486 const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT);
488 for (int i = 1; i <= total_control_points + 2; i++) {
489 float parm = 1.0f * i / (total_control_points + 2 + 1);
490 if (endpoint) {
491 if (i <= nurbs_degree) {
492 parm = 0;
493 }
494 else if (i > total_control_points + 2 - nurbs_degree) {
495 parm = 1;
496 }
497 }
498 fh.write_obj_nurbs_parm(parm);
499 }
502 }
503}
504
505/* -------------------------------------------------------------------- */
509static const char *tex_map_type_to_string[] = {
510 "map_Kd",
511 "map_Pm",
512 "map_Ks",
513 "map_Ns",
514 "map_Pr",
515 "map_Ps",
516 "map_refl",
517 "map_Ke",
518 "map_d",
519 "map_Bump",
520};
522 "array size mismatch");
523
528static std::string float3_to_string(const float3 &numbers)
529{
530 std::ostringstream r_string;
531 r_string << numbers[0] << " " << numbers[1] << " " << numbers[2];
532 return r_string.str();
533};
534
535MTLWriter::MTLWriter(const char *obj_filepath, bool write_file) noexcept(false)
536{
537 if (!write_file) {
538 return;
539 }
540 char mtl_path[FILE_MAX];
541 STRNCPY(mtl_path, obj_filepath);
542
543 const bool ok = BLI_path_extension_replace(mtl_path, sizeof(mtl_path), ".mtl");
544 if (!ok) {
545 throw std::system_error(ENAMETOOLONG, std::system_category(), "");
546 }
547
548 mtl_filepath_ = mtl_path;
549 outfile_ = BLI_fopen(mtl_filepath_.c_str(), "wb");
550 if (!outfile_) {
551 throw std::system_error(errno, std::system_category(), "Cannot open file " + mtl_filepath_);
552 }
553}
555{
556 if (outfile_) {
557 fmt_handler_.write_to_file(outfile_);
558 if (std::fclose(outfile_)) {
559 std::cerr << "Error: could not close the file '" << mtl_filepath_
560 << "' properly, it may be corrupted." << std::endl;
561 }
562 }
563}
564
565void MTLWriter::write_header(const char *blen_filepath)
566{
567 using namespace std::string_literals;
568 const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
569 BLI_path_basename(blen_filepath) :
570 "None";
571 fmt_handler_.write_string("# Blender "s + BKE_blender_version_string() + " MTL File: '" +
572 blen_basename + "'");
573 fmt_handler_.write_string("# www.blender.org");
574}
575
577{
578 return mtl_filepath_;
579}
580
581void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl, bool write_pbr)
582{
583 /* For various material properties, we only capture information
584 * coming from the texture, or the default value of the socket.
585 * When the texture is present, do not emit the default value. */
586
587 /* Do not write Ns & Ka when writing in PBR mode. */
588 if (!write_pbr) {
590 fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent);
591 }
592 fmt_handler_.write_mtl_float3(
593 "Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z);
594 }
596 fmt_handler_.write_mtl_float3("Kd", mtl.color.x, mtl.color.y, mtl.color.z);
597 }
599 fmt_handler_.write_mtl_float3("Ks", mtl.spec_color.x, mtl.spec_color.y, mtl.spec_color.z);
600 }
602 fmt_handler_.write_mtl_float3(
603 "Ke", mtl.emission_color.x, mtl.emission_color.y, mtl.emission_color.z);
604 }
605 fmt_handler_.write_mtl_float("Ni", mtl.ior);
607 fmt_handler_.write_mtl_float("d", mtl.alpha);
608 }
609 fmt_handler_.write_mtl_illum(mtl.illum_mode);
610
611 if (write_pbr) {
612 if (!mtl.tex_map_of_type(MTLTexMapType::Roughness).is_valid() && mtl.roughness >= 0.0f) {
613 fmt_handler_.write_mtl_float("Pr", mtl.roughness);
614 }
615 if (!mtl.tex_map_of_type(MTLTexMapType::Metallic).is_valid() && mtl.metallic >= 0.0f) {
616 fmt_handler_.write_mtl_float("Pm", mtl.metallic);
617 }
618 if (!mtl.tex_map_of_type(MTLTexMapType::Sheen).is_valid() && mtl.sheen >= 0.0f) {
619 fmt_handler_.write_mtl_float("Ps", mtl.sheen);
620 }
621 if (mtl.cc_thickness >= 0.0f) {
622 fmt_handler_.write_mtl_float("Pc", mtl.cc_thickness);
623 }
624 if (mtl.cc_roughness >= 0.0f) {
625 fmt_handler_.write_mtl_float("Pcr", mtl.cc_roughness);
626 }
627 if (mtl.aniso >= 0.0f) {
628 fmt_handler_.write_mtl_float("aniso", mtl.aniso);
629 }
630 if (mtl.aniso_rot >= 0.0f) {
631 fmt_handler_.write_mtl_float("anisor", mtl.aniso_rot);
632 }
633 if (mtl.transmit_color.x > 0.0f || mtl.transmit_color.y > 0.0f || mtl.transmit_color.z > 0.0f)
634 {
635 fmt_handler_.write_mtl_float3(
636 "Tf", mtl.transmit_color.x, mtl.transmit_color.y, mtl.transmit_color.z);
637 }
638 }
639}
640
641void MTLWriter::write_texture_map(const MTLMaterial &mtl_material,
642 MTLTexMapType texture_key,
643 const MTLTexMap &texture_map,
644 const char *blen_filedir,
645 const char *dest_dir,
646 ePathReferenceMode path_mode,
647 Set<std::pair<std::string, std::string>> &copy_set)
648{
649 std::string options;
650 /* Option strings should have their own leading spaces. */
651 if (texture_map.translation != float3{0.0f, 0.0f, 0.0f}) {
652 options.append(" -o ").append(float3_to_string(texture_map.translation));
653 }
654 if (texture_map.scale != float3{1.0f, 1.0f, 1.0f}) {
655 options.append(" -s ").append(float3_to_string(texture_map.scale));
656 }
657 if (texture_key == MTLTexMapType::Normal && mtl_material.normal_strength > 0.0001f) {
658 options.append(" -bm ").append(std::to_string(mtl_material.normal_strength));
659 }
660
661 std::string path = path_reference(
662 texture_map.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set);
663 /* Always emit forward slashes for cross-platform compatibility. */
664 std::replace(path.begin(), path.end(), '\\', '/');
665
666 fmt_handler_.write_mtl_map(tex_map_type_to_string[int(texture_key)], options, path);
667}
668
669static bool is_pbr_map(MTLTexMapType type)
670{
671 return type == MTLTexMapType::Metallic || type == MTLTexMapType::Roughness ||
672 type == MTLTexMapType::Sheen;
673}
674
676{
678}
679
680void MTLWriter::write_materials(const char *blen_filepath,
681 ePathReferenceMode path_mode,
682 const char *dest_dir,
683 bool write_pbr)
684{
685 if (mtlmaterials_.is_empty()) {
686 return;
687 }
688
689 char blen_filedir[FILE_MAX];
690 BLI_path_split_dir_part(blen_filepath, blen_filedir, sizeof(blen_filedir));
691 BLI_path_slash_native(blen_filedir);
692 BLI_path_normalize(blen_filedir);
693
694 std::sort(mtlmaterials_.begin(),
695 mtlmaterials_.end(),
696 [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
698 for (const MTLMaterial &mtlmat : mtlmaterials_) {
699 fmt_handler_.write_string("");
700 fmt_handler_.write_mtl_newmtl(mtlmat.name);
701 write_bsdf_properties(mtlmat, write_pbr);
702 for (int key = 0; key < int(MTLTexMapType::Count); key++) {
703 const MTLTexMap &tex = mtlmat.texture_maps[key];
704 if (!tex.is_valid()) {
705 continue;
706 }
707 if (!write_pbr && is_pbr_map((MTLTexMapType)key)) {
708 continue;
709 }
710 if (write_pbr && is_non_pbr_map((MTLTexMapType)key)) {
711 continue;
712 }
713 write_texture_map(
714 mtlmat, (MTLTexMapType)key, tex, blen_filedir, dest_dir, path_mode, copy_set);
715 }
716 }
717 path_reference_copy(copy_set);
718}
719
721{
722 Vector<int> r_mtl_indices;
723 r_mtl_indices.resize(mesh_to_export.tot_materials());
724 for (int16_t i = 0; i < mesh_to_export.tot_materials(); i++) {
725 const Material *material = mesh_to_export.materials[i];
726 if (!material) {
727 r_mtl_indices[i] = -1;
728 continue;
729 }
730 int mtlmat_index = material_map_.lookup_default(material, -1);
731 if (mtlmat_index != -1) {
732 r_mtl_indices[i] = mtlmat_index;
733 }
734 else {
735 mtlmaterials_.append(mtlmaterial_for_material(material));
736 r_mtl_indices[i] = mtlmaterials_.size() - 1;
737 material_map_.add_new(material, r_mtl_indices[i]);
738 }
739 }
740 return r_mtl_indices;
741}
742
743const char *MTLWriter::mtlmaterial_name(int index)
744{
745 if (index < 0 || index >= mtlmaterials_.size()) {
746 return nullptr;
747 }
748 return mtlmaterials_[index].name.c_str();
749}
752} // namespace blender::io::obj
const char * BKE_blender_version_string(void)
Definition blender.cc:139
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAXFILE
#define FILE_MAX
bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
void BLI_path_slash_native(char *path) ATTR_NONNULL(1)
int BLI_path_normalize(char *path) ATTR_NONNULL(1)
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
#define FILE_MAXDIR
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define ARRAY_SIZE(arr)
@ CU_NURB_CYCLIC
@ CU_NURB_ENDPOINT
ePathReferenceMode
AttributeSet attributes
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const char * data() const
void resize(const int64_t new_size)
void write_mtl_map(const char *type, StringRef options, StringRef value)
void write_obj_face_v_uv_normal(int v, int uv, int n)
void write_mtl_float3(const char *type, float r, float g, float b)
void write_obj_vertex(float x, float y, float z)
void write_obj_vertex_color(float x, float y, float z, float r, float g, float b)
void write_mtl_float(const char *type, float v)
void append_from(FormatHandler &v)
MTLWriter(const char *obj_filepath, bool write_file) 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, bool write_pbr)
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
float3 vertex_coordinates(int spline_index, int vertex_index, float global_scale) const
int get_nurbs_degree(int spline_index) const
const char * get_curve_name() const
const char * get_face_deform_group_name(int16_t def_group_index) const
int16_t get_face_deform_group_index(int face_index, MutableSpan< float > group_weights) const
StringRef get_object_mesh_name() const
Span< int > get_face_uv_indices(const int face_index) const
int remap_face_index(int i) const
Array< const Material * > materials
Span< float3 > get_normal_coords() const
const float4x4 & get_world_axes_transform() const
StringRef get_object_name() const
const Span< float2 > get_uv_coords() const
Span< int > get_face_normal_indices(const int face_index) const
Span< int > calc_face_vert_indices(const int face_index) const
const Mesh * get_mesh() const
void write_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
void write_uv_coords(FormatHandler &fh, OBJMesh &obj_mesh_data) const
void write_mtllib_name(const StringRefNull mtl_filepath) const
void write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const
void write_face_elements(FormatHandler &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data, FunctionRef< const char *(int)> matname_fn)
void write_vertex_coords(FormatHandler &fh, const OBJMesh &obj_mesh_data, bool write_colors) const
void write_edges_indices(FormatHandler &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data) const
void write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const
local_group_size(16, 16) .push_constant(Type b
CCL_NAMESPACE_BEGIN struct Options options
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
IndexRange range
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
static void spaces_to_underscores(std::string &r_name)
static bool is_pbr_map(MTLTexMapType type)
static const char * tex_map_type_to_string[]
static const char * MATERIAL_GROUP_DISABLED
MTLMaterial mtlmaterial_for_material(const Material *material)
static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params, int face_idx)
void obj_parallel_chunked_output(FormatHandler &fh, int tot_count, const Function &function)
static bool is_non_pbr_map(MTLTexMapType type)
static std::string float3_to_string(const float3 &numbers)
static const int chunk_size
static int calc_chunk_count(int count)
static const char * DEFORM_GROUP_DISABLED
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:12
void path_reference_copy(const Set< std::pair< std::string, std::string > > &copy_set)
Definition path_util.cc:61
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
signed short int16_t
Definition stdint.h:76
__int64 int64_t
Definition stdint.h:89
blender::BitVector is_loose_bits
const MTLTexMap & tex_map_of_type(MTLTexMapType key) const
char * buffers[2]