Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc')
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc626
1 files changed, 626 insertions, 0 deletions
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
new file mode 100644
index 00000000000..d92d1c5ad48
--- /dev/null
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -0,0 +1,626 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup obj
+ */
+
+#include <algorithm>
+#include <cstdio>
+
+#include "BKE_blender_version.h"
+
+#include "BLI_path_util.h"
+
+#include "obj_export_mesh.hh"
+#include "obj_export_mtl.hh"
+#include "obj_export_nurbs.hh"
+
+#include "obj_export_file_writer.hh"
+
+namespace blender::io::obj {
+/**
+ * Per reference http://www.martinreddy.net/gfx/3d/OBJ.spec:
+ * To turn off smoothing groups, use a value of 0 or off.
+ * Polygonal elements use group numbers to put elements in different smoothing groups.
+ * For free-form surfaces, smoothing groups are either turned on or off;
+ * there is no difference between values greater than 0.
+ */
+const int SMOOTH_GROUP_DISABLED = 0;
+const int SMOOTH_GROUP_DEFAULT = 1;
+
+const char *DEFORM_GROUP_DISABLED = "off";
+/* There is no deform group default name. Use what the user set in the UI. */
+
+/**
+ * Per reference http://www.martinreddy.net/gfx/3d/OBJ.spec:
+ * Once a material is assigned, it cannot be turned off; it can only be changed.
+ * If a material name is not specified, a white material is used.
+ * So an empty material name is written. */
+const char *MATERIAL_GROUP_DISABLED = "";
+
+/**
+ * Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
+ */
+void OBJWriter::write_vert_uv_normal_indices(Span<int> vert_indices,
+ Span<int> uv_indices,
+ Span<int> normal_indices) const
+{
+ BLI_assert(vert_indices.size() == uv_indices.size() &&
+ vert_indices.size() == normal_indices.size());
+ file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ for (int j = 0; j < vert_indices.size(); j++) {
+ file_handler_->write<eOBJSyntaxElement::vertex_uv_normal_indices>(
+ vert_indices[j] + index_offsets_.vertex_offset + 1,
+ uv_indices[j] + index_offsets_.uv_vertex_offset + 1,
+ normal_indices[j] + index_offsets_.normal_offset + 1);
+ }
+ file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+}
+
+/**
+ * Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
+ */
+void OBJWriter::write_vert_normal_indices(Span<int> vert_indices,
+ Span<int> /*uv_indices*/,
+ Span<int> normal_indices) const
+{
+ BLI_assert(vert_indices.size() == normal_indices.size());
+ file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ for (int j = 0; j < vert_indices.size(); j++) {
+ file_handler_->write<eOBJSyntaxElement::vertex_normal_indices>(
+ vert_indices[j] + index_offsets_.vertex_offset + 1,
+ normal_indices[j] + index_offsets_.normal_offset + 1);
+ }
+ file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+}
+
+/**
+ * Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
+ */
+void OBJWriter::write_vert_uv_indices(Span<int> vert_indices,
+ Span<int> uv_indices,
+ Span<int> /*normal_indices*/) const
+{
+ BLI_assert(vert_indices.size() == uv_indices.size());
+ file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ for (int j = 0; j < vert_indices.size(); j++) {
+ file_handler_->write<eOBJSyntaxElement::vertex_uv_indices>(
+ vert_indices[j] + index_offsets_.vertex_offset + 1,
+ uv_indices[j] + index_offsets_.uv_vertex_offset + 1);
+ }
+ file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+}
+
+/**
+ * Write one line of polygon indices as "f v1 v2 ...".
+ */
+void OBJWriter::write_vert_indices(Span<int> vert_indices,
+ Span<int> /*uv_indices*/,
+ Span<int> /*normal_indices*/) const
+{
+ file_handler_->write<eOBJSyntaxElement::poly_element_begin>();
+ for (const int vert_index : vert_indices) {
+ file_handler_->write<eOBJSyntaxElement::vertex_indices>(vert_index +
+ index_offsets_.vertex_offset + 1);
+ }
+ file_handler_->write<eOBJSyntaxElement::poly_element_end>();
+}
+
+void OBJWriter::write_header() const
+{
+ using namespace std::string_literals;
+ file_handler_->write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
+ "\n");
+ file_handler_->write<eOBJSyntaxElement::string>("# www.blender.org\n");
+}
+
+/**
+ * Write file name of Material Library in .OBJ file.
+ */
+void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
+{
+ /* Split .MTL file path into parent directory and filename. */
+ char mtl_file_name[FILE_MAXFILE];
+ char mtl_dir_name[FILE_MAXDIR];
+ BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
+ file_handler_->write<eOBJSyntaxElement::mtllib>(mtl_file_name);
+}
+
+/**
+ * Write an object's group with mesh and/or material name appended conditionally.
+ */
+void OBJWriter::write_object_group(const OBJMesh &obj_mesh_data) const
+{
+ /* "o object_name" is not mandatory. A valid .OBJ file may contain neither
+ * "o name" nor "g group_name". */
+ BLI_assert(export_params_.export_object_groups);
+ if (!export_params_.export_object_groups) {
+ return;
+ }
+ const std::string object_name = obj_mesh_data.get_object_name();
+ const char *object_mesh_name = obj_mesh_data.get_object_mesh_name();
+ const char *object_material_name = obj_mesh_data.get_object_material_name(0);
+ if (export_params_.export_materials && export_params_.export_material_groups &&
+ object_material_name) {
+ file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name +
+ "_" + object_material_name);
+ return;
+ }
+ file_handler_->write<eOBJSyntaxElement::object_group>(object_name + "_" + object_mesh_name);
+}
+
+/**
+ * Write object's name or group.
+ */
+void OBJWriter::write_object_name(const OBJMesh &obj_mesh_data) const
+{
+ const char *object_name = obj_mesh_data.get_object_name();
+ if (export_params_.export_object_groups) {
+ write_object_group(obj_mesh_data);
+ return;
+ }
+ file_handler_->write<eOBJSyntaxElement::object_name>(object_name);
+}
+
+/**
+ * Write vertex coordinates for all vertices as "v x y z".
+ */
+void OBJWriter::write_vertex_coords(const OBJMesh &obj_mesh_data) const
+{
+ const int tot_vertices = obj_mesh_data.tot_vertices();
+ for (int i = 0; i < tot_vertices; i++) {
+ float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
+ file_handler_->write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
+ }
+}
+
+/**
+ * Write UV vertex coordinates for all vertices as "vt u v".
+ * \note UV indices are stored here, but written later.
+ */
+void OBJWriter::write_uv_coords(OBJMesh &r_obj_mesh_data) const
+{
+ Vector<std::array<float, 2>> uv_coords;
+ /* UV indices are calculated and stored in an OBJMesh member here. */
+ r_obj_mesh_data.store_uv_coords_and_indices(uv_coords);
+
+ for (const std::array<float, 2> &uv_vertex : uv_coords) {
+ file_handler_->write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
+ }
+}
+
+/**
+ * Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
+ */
+void OBJWriter::write_poly_normals(const OBJMesh &obj_mesh_data) const
+{
+ obj_mesh_data.ensure_mesh_normals();
+ Vector<float3> lnormals;
+ const int tot_polygons = obj_mesh_data.tot_polygons();
+ for (int i = 0; i < tot_polygons; i++) {
+ if (obj_mesh_data.is_ith_poly_smooth(i)) {
+ obj_mesh_data.calc_loop_normals(i, lnormals);
+ for (const float3 &lnormal : lnormals) {
+ file_handler_->write<eOBJSyntaxElement::normal>(lnormal[0], lnormal[1], lnormal[2]);
+ }
+ }
+ else {
+ float3 poly_normal = obj_mesh_data.calc_poly_normal(i);
+ file_handler_->write<eOBJSyntaxElement::normal>(
+ poly_normal[0], poly_normal[1], poly_normal[2]);
+ }
+ }
+}
+
+/**
+ * Write smooth group if polygon at the given index is shaded smooth else "s 0"
+ */
+int OBJWriter::write_smooth_group(const OBJMesh &obj_mesh_data,
+ const int poly_index,
+ const int last_poly_smooth_group) const
+{
+ int current_group = SMOOTH_GROUP_DISABLED;
+ if (!export_params_.export_smooth_groups && obj_mesh_data.is_ith_poly_smooth(poly_index)) {
+ /* Smooth group calculation is disabled, but polygon is smooth-shaded. */
+ current_group = SMOOTH_GROUP_DEFAULT;
+ }
+ else if (obj_mesh_data.is_ith_poly_smooth(poly_index)) {
+ /* Smooth group calc is enabled and polygon is smooth–shaded, so find the group. */
+ current_group = obj_mesh_data.ith_smooth_group(poly_index);
+ }
+
+ if (current_group == last_poly_smooth_group) {
+ /* Group has already been written, even if it is "s 0". */
+ return current_group;
+ }
+ file_handler_->write<eOBJSyntaxElement::smooth_group>(current_group);
+ return current_group;
+}
+
+/**
+ * Write material name and material group of a polygon in the .OBJ file.
+ * \return #mat_nr of the polygon at the given index.
+ * \note It doesn't write to the material library.
+ */
+int16_t OBJWriter::write_poly_material(const OBJMesh &obj_mesh_data,
+ const int poly_index,
+ const int16_t last_poly_mat_nr,
+ std::function<const char *(int)> matname_fn) const
+{
+ if (!export_params_.export_materials || obj_mesh_data.tot_materials() <= 0) {
+ return last_poly_mat_nr;
+ }
+ const int16_t current_mat_nr = obj_mesh_data.ith_poly_matnr(poly_index);
+ /* Whenever a polygon with a new material is encountered, write its material
+ * and/or group, otherwise pass. */
+ if (last_poly_mat_nr == current_mat_nr) {
+ return current_mat_nr;
+ }
+ if (current_mat_nr == NOT_FOUND) {
+ file_handler_->write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
+ return current_mat_nr;
+ }
+ if (export_params_.export_object_groups) {
+ write_object_group(obj_mesh_data);
+ }
+ const char *mat_name = matname_fn(current_mat_nr);
+ if (!mat_name) {
+ mat_name = MATERIAL_GROUP_DISABLED;
+ }
+ file_handler_->write<eOBJSyntaxElement::poly_usemtl>(mat_name);
+
+ return current_mat_nr;
+}
+
+/**
+ * Write the name of the deform group of a polygon.
+ */
+int16_t OBJWriter::write_vertex_group(const OBJMesh &obj_mesh_data,
+ const int poly_index,
+ const int16_t last_poly_vertex_group) const
+{
+ if (!export_params_.export_vertex_groups) {
+ return last_poly_vertex_group;
+ }
+ const int16_t current_group = obj_mesh_data.get_poly_deform_group_index(poly_index);
+
+ if (current_group == last_poly_vertex_group) {
+ /* No vertex group found in this polygon, just like in the last iteration. */
+ return current_group;
+ }
+ if (current_group == NOT_FOUND) {
+ file_handler_->write<eOBJSyntaxElement::object_group>(DEFORM_GROUP_DISABLED);
+ return current_group;
+ }
+ file_handler_->write<eOBJSyntaxElement::object_group>(
+ obj_mesh_data.get_poly_deform_group_name(current_group));
+ return current_group;
+}
+
+/**
+ * \return Writer function with appropriate polygon-element syntax.
+ */
+OBJWriter::func_vert_uv_normal_indices OBJWriter::get_poly_element_writer(
+ const int total_uv_vertices) const
+{
+ if (export_params_.export_normals) {
+ if (export_params_.export_uv && (total_uv_vertices > 0)) {
+ /* Write both normals and UV indices. */
+ return &OBJWriter::write_vert_uv_normal_indices;
+ }
+ /* Write normals indices. */
+ return &OBJWriter::write_vert_normal_indices;
+ }
+ /* Write UV indices. */
+ if (export_params_.export_uv && (total_uv_vertices > 0)) {
+ return &OBJWriter::write_vert_uv_indices;
+ }
+ /* Write neither normals nor UV indices. */
+ return &OBJWriter::write_vert_indices;
+}
+
+/**
+ * Write polygon elements with at least vertex indices, and conditionally with UV vertex
+ * indices and polygon normal indices. Also write groups: smooth, vertex, material.
+ * The matname_fn turns a 0-indexed material slot number in an Object into the
+ * name used in the .obj file.
+ * \note UV indices were stored while writing UV vertices.
+ */
+void OBJWriter::write_poly_elements(const OBJMesh &obj_mesh_data,
+ std::function<const char *(int)> matname_fn)
+{
+ int last_poly_smooth_group = NEGATIVE_INIT;
+ int16_t last_poly_vertex_group = NEGATIVE_INIT;
+ int16_t last_poly_mat_nr = NEGATIVE_INIT;
+
+ const func_vert_uv_normal_indices poly_element_writer = get_poly_element_writer(
+ obj_mesh_data.tot_uv_vertices());
+
+ /* Number of normals may not be equal to number of polygons due to smooth shading. */
+ int per_object_tot_normals = 0;
+ const int tot_polygons = obj_mesh_data.tot_polygons();
+ for (int i = 0; i < tot_polygons; i++) {
+ Vector<int> poly_vertex_indices = obj_mesh_data.calc_poly_vertex_indices(i);
+ Span<int> poly_uv_indices = obj_mesh_data.calc_poly_uv_indices(i);
+ /* For an Object, a normal index depends on how many of its normals have been written before
+ * it. This is unknown because of smooth shading. So pass "per object total normals"
+ * and update it after each call. */
+ int new_normals = 0;
+ Vector<int> poly_normal_indices;
+ std::tie(new_normals, poly_normal_indices) = obj_mesh_data.calc_poly_normal_indices(
+ i, per_object_tot_normals);
+ per_object_tot_normals += new_normals;
+
+ last_poly_smooth_group = write_smooth_group(obj_mesh_data, i, last_poly_smooth_group);
+ last_poly_vertex_group = write_vertex_group(obj_mesh_data, i, last_poly_vertex_group);
+ last_poly_mat_nr = write_poly_material(obj_mesh_data, i, last_poly_mat_nr, matname_fn);
+ (this->*poly_element_writer)(poly_vertex_indices, poly_uv_indices, poly_normal_indices);
+ }
+ /* Unusual: Other indices are updated in #OBJWriter::update_index_offsets. */
+ index_offsets_.normal_offset += per_object_tot_normals;
+}
+
+/**
+ * Write loose edges of a mesh as "l v1 v2".
+ */
+void OBJWriter::write_edges_indices(const OBJMesh &obj_mesh_data) const
+{
+ obj_mesh_data.ensure_mesh_edges();
+ const int tot_edges = obj_mesh_data.tot_edges();
+ for (int edge_index = 0; edge_index < tot_edges; edge_index++) {
+ const std::optional<std::array<int, 2>> vertex_indices =
+ obj_mesh_data.calc_loose_edge_vert_indices(edge_index);
+ if (!vertex_indices) {
+ continue;
+ }
+ file_handler_->write<eOBJSyntaxElement::edge>(
+ (*vertex_indices)[0] + index_offsets_.vertex_offset + 1,
+ (*vertex_indices)[1] + index_offsets_.vertex_offset + 1);
+ }
+}
+
+/**
+ * Write a NURBS curve to the .OBJ file in parameter form.
+ */
+void OBJWriter::write_nurbs_curve(const OBJCurve &obj_nurbs_data) const
+{
+ const int total_splines = obj_nurbs_data.total_splines();
+ for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
+ const int total_vertices = obj_nurbs_data.total_spline_vertices(spline_idx);
+ for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
+ const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
+ spline_idx, vertex_idx, export_params_.scaling_factor);
+ file_handler_->write<eOBJSyntaxElement::vertex_coords>(
+ vertex_coords[0], vertex_coords[1], vertex_coords[2]);
+ }
+
+ const char *nurbs_name = obj_nurbs_data.get_curve_name();
+ const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
+ file_handler_->write<eOBJSyntaxElement::object_group>(nurbs_name);
+ file_handler_->write<eOBJSyntaxElement::cstype>();
+ file_handler_->write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
+ /**
+ * The numbers written here are indices into the vertex coordinates written
+ * earlier, relative to the line that is going to be written.
+ * [0.0 - 1.0] is the curve parameter range.
+ * 0.0 1.0 -1 -2 -3 -4 for a non-cyclic curve with 4 vertices.
+ * 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices.
+ */
+ const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
+ file_handler_->write<eOBJSyntaxElement::curve_element_begin>();
+ for (int i = 0; i < total_control_points; i++) {
+ /* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
+ * last vertex coordinate, -2 second last. */
+ file_handler_->write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
+ }
+ file_handler_->write<eOBJSyntaxElement::curve_element_end>();
+
+ /**
+ * In "parm u 0 0.1 .." line:, (total control points + 2) equidistant numbers in the
+ * parameter range are inserted.
+ */
+ file_handler_->write<eOBJSyntaxElement::nurbs_parameter_begin>();
+ for (int i = 1; i <= total_control_points + 2; i++) {
+ file_handler_->write<eOBJSyntaxElement::nurbs_parameters>(1.0f * i /
+ (total_control_points + 2 + 1));
+ }
+ file_handler_->write<eOBJSyntaxElement::nurbs_parameter_end>();
+
+ file_handler_->write<eOBJSyntaxElement::nurbs_group_end>();
+ }
+}
+
+/**
+ * When there are multiple objects in a frame, the indices of previous objects' coordinates or
+ * normals add up.
+ */
+void OBJWriter::update_index_offsets(const OBJMesh &obj_mesh_data)
+{
+ index_offsets_.vertex_offset += obj_mesh_data.tot_vertices();
+ index_offsets_.uv_vertex_offset += obj_mesh_data.tot_uv_vertices();
+ /* Normal index is updated right after writing the normals. */
+}
+
+/* -------------------------------------------------------------------- */
+/** \name .MTL writers.
+ * \{ */
+
+/**
+ * Convert #float3 to string of space-separated numbers, with no leading or trailing space.
+ * Only to be used in NON-performance-critical code.
+ */
+static std::string float3_to_string(const float3 &numbers)
+{
+ std::ostringstream r_string;
+ r_string << numbers[0] << " " << numbers[1] << " " << numbers[2];
+ return r_string.str();
+};
+
+/*
+ * Create the .MTL file.
+ */
+MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false)
+{
+ mtl_filepath_ = obj_filepath;
+ const bool ok = BLI_path_extension_replace(mtl_filepath_.data(), FILE_MAX, ".mtl");
+ if (!ok) {
+ throw std::system_error(ENAMETOOLONG, std::system_category(), "");
+ }
+ file_handler_ = std::make_unique<FileHandler<eFileType::MTL>>(mtl_filepath_);
+}
+
+void MTLWriter::write_header(const char *blen_filepath) const
+{
+ using namespace std::string_literals;
+ const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
+ BLI_path_basename(blen_filepath) :
+ "None";
+ file_handler_->write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
+ " MTL File: '" + blen_basename + "'\n");
+ file_handler_->write<eMTLSyntaxElement::string>("# www.blender.org\n");
+}
+
+StringRefNull MTLWriter::mtl_file_path() const
+{
+ return mtl_filepath_;
+}
+
+/**
+ * Write properties sourced from p-BSDF node or #Object.Material.
+ */
+void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
+{
+ file_handler_->write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
+ file_handler_->write<eMTLSyntaxElement::Ka>(
+ mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
+ file_handler_->write<eMTLSyntaxElement::Kd>(
+ mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
+ file_handler_->write<eMTLSyntaxElement::Ks>(
+ mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
+ file_handler_->write<eMTLSyntaxElement::Ke>(
+ mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
+ file_handler_->write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
+ file_handler_->write<eMTLSyntaxElement::d>(mtl_material.d);
+ file_handler_->write<eMTLSyntaxElement::illum>(mtl_material.illum);
+}
+
+/**
+ * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image".
+ */
+void MTLWriter::write_texture_map(
+ const MTLMaterial &mtl_material,
+ const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map)
+{
+ std::string translation;
+ std::string scale;
+ std::string map_bump_strength;
+ /* Optional strings should have their own leading spaces. */
+ if (texture_map.value.translation != float3{0.0f, 0.0f, 0.0f}) {
+ translation.append(" -s ").append(float3_to_string(texture_map.value.translation));
+ }
+ if (texture_map.value.scale != float3{1.0f, 1.0f, 1.0f}) {
+ scale.append(" -o ").append(float3_to_string(texture_map.value.scale));
+ }
+ if (texture_map.key == eMTLSyntaxElement::map_Bump && mtl_material.map_Bump_strength > 0.0001f) {
+ map_bump_strength.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
+ }
+
+#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
+ if (texture_map.key == eMTLSyntaxElement) { \
+ file_handler_->write<eMTLSyntaxElement>(translation + scale + map_bump_strength, \
+ texture_map.value.image_path); \
+ return; \
+ }
+
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_Kd);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ks);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ns);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_d);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke);
+ SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump);
+
+ BLI_assert(!"This map type was not written to the file.");
+}
+
+/**
+ * Write all of the material specifications to the MTL file.
+ * For consistency of output from run to run (useful for testing),
+ * the materials are sorted by name before writing.
+ */
+void MTLWriter::write_materials()
+{
+ if (mtlmaterials_.size() == 0) {
+ return;
+ }
+ std::sort(mtlmaterials_.begin(),
+ mtlmaterials_.end(),
+ [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
+ for (const MTLMaterial &mtlmat : mtlmaterials_) {
+ file_handler_->write<eMTLSyntaxElement::string>("\n");
+ file_handler_->write<eMTLSyntaxElement::newmtl>(mtlmat.name);
+ write_bsdf_properties(mtlmat);
+ for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map :
+ mtlmat.texture_maps.items()) {
+ if (!texture_map.value.image_path.empty()) {
+ write_texture_map(mtlmat, texture_map);
+ }
+ }
+ }
+}
+
+/**
+ * Add the materials of the given object to MTLWriter, deduping
+ * against ones that are already there.
+ * Return a Vector of indices into mtlmaterials_ that hold the MTLMaterial
+ * that corresponds to each material slot, in order, of the given Object.
+ * Indexes are returned rather than pointers to the MTLMaterials themselves
+ * because the mtlmaterials_ Vector may move around when resized.
+ */
+Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export)
+{
+ Vector<int> r_mtl_indices;
+ r_mtl_indices.resize(mesh_to_export.tot_materials());
+ for (int16_t i = 0; i < mesh_to_export.tot_materials(); i++) {
+ const Material *material = mesh_to_export.get_object_material(i);
+ if (!material) {
+ r_mtl_indices[i] = -1;
+ continue;
+ }
+ int mtlmat_index = material_map_.lookup_default(material, -1);
+ if (mtlmat_index != -1) {
+ r_mtl_indices[i] = mtlmat_index;
+ }
+ else {
+ mtlmaterials_.append(mtlmaterial_for_material(material));
+ r_mtl_indices[i] = mtlmaterials_.size() - 1;
+ material_map_.add_new(material, r_mtl_indices[i]);
+ }
+ }
+ return r_mtl_indices;
+}
+
+const char *MTLWriter::mtlmaterial_name(int index)
+{
+ if (index < 0 || index >= mtlmaterials_.size()) {
+ return nullptr;
+ }
+ return mtlmaterials_[index].name.c_str();
+}
+/** \} */
+
+} // namespace blender::io::obj