diff options
Diffstat (limited to 'source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc')
-rw-r--r-- | source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc | 214 |
1 files changed, 129 insertions, 85 deletions
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index 4ed148ec64e..0231bc79b89 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -6,6 +6,7 @@ #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BLI_map.hh" #include "BLI_math_vector.h" @@ -15,13 +16,26 @@ #include "DNA_material_types.h" #include "DNA_node_types.h" -#include "NOD_node_tree_ref.hh" - #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" namespace blender::io::obj { +const char *tex_map_type_to_socket_id[] = { + "Base Color", + "Metallic", + "Specular", + "Roughness", /* Map specular exponent to roughness. */ + "Roughness", + "Sheen", + "Metallic", /* Map reflection to metallic. */ + "Emission", + "Alpha", + "Normal", +}; +BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_socket_id) == int(MTLTexMapType::Count), + "array size mismatch"); + /** * Copy a float property of the given type from the bNode to given buffer. */ @@ -72,25 +86,25 @@ static void copy_property_from_node(const eNodeSocketDatatype property_type, * Collect all the source sockets linked to the destination socket in a destination node. */ static void linked_sockets_to_dest_id(const bNode *dest_node, - const nodes::NodeTreeRef &node_tree, - StringRefNull dest_socket_id, - Vector<const nodes::OutputSocketRef *> &r_linked_sockets) + const bNodeTree &node_tree, + const char *dest_socket_id, + Vector<const bNodeSocket *> &r_linked_sockets) { r_linked_sockets.clear(); if (!dest_node) { return; } - Span<const nodes::NodeRef *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname); - Span<const nodes::InputSocketRef *> dest_inputs = object_dest_nodes.first()->inputs(); - const nodes::InputSocketRef *dest_socket = nullptr; - for (const nodes::InputSocketRef *curr_socket : dest_inputs) { - if (STREQ(curr_socket->bsocket()->identifier, dest_socket_id.c_str())) { + Span<const bNode *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname); + Span<const bNodeSocket *> dest_inputs = object_dest_nodes.first()->input_sockets(); + const bNodeSocket *dest_socket = nullptr; + for (const bNodeSocket *curr_socket : dest_inputs) { + if (STREQ(curr_socket->identifier, dest_socket_id)) { dest_socket = curr_socket; break; } } if (dest_socket) { - Span<const nodes::OutputSocketRef *> linked_sockets = dest_socket->directly_linked_sockets(); + Span<const bNodeSocket *> linked_sockets = dest_socket->directly_linked_sockets(); r_linked_sockets.resize(linked_sockets.size()); r_linked_sockets = linked_sockets; } @@ -99,40 +113,52 @@ static void linked_sockets_to_dest_id(const bNode *dest_node, /** * From a list of sockets, get the parent node which is of the given node type. */ -static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> sockets_list, - const int node_type) +static const bNode *get_node_of_type(Span<const bNodeSocket *> sockets_list, const int node_type) { - for (const nodes::SocketRef *socket : sockets_list) { - const bNode *parent_node = socket->bnode(); - if (parent_node->typeinfo->type == node_type) { - return parent_node; + for (const bNodeSocket *socket : sockets_list) { + const bNode &parent_node = socket->owner_node(); + if (parent_node.typeinfo->type == node_type) { + return &parent_node; } } return nullptr; } -/** +/* * From a texture image shader node, get the image's filepath. * If packed image is found, only the file "name" is returned. */ -static const char *get_image_filepath(const bNode *tex_node) +static std::string get_image_filepath(const bNode *tex_node) { if (!tex_node) { - return nullptr; + return ""; } Image *tex_image = reinterpret_cast<Image *>(tex_node->id); if (!tex_image || !BKE_image_has_filepath(tex_image)) { - return nullptr; + return ""; } - const char *path = tex_image->filepath; + if (BKE_image_has_packedfile(tex_image)) { /* Put image in the same directory as the .MTL file. */ - path = BLI_path_slash_rfind(path) + 1; + const char *filename = BLI_path_slash_rfind(tex_image->filepath) + 1; fprintf(stderr, "Packed image found:'%s'. Unpack and place the image in the same " "directory as the .MTL file.\n", - path); + filename); + return filename; } + + char path[FILE_MAX]; + BLI_strncpy(path, tex_image->filepath, FILE_MAX); + + if (tex_image->source == IMA_SRC_SEQUENCE) { + char head[FILE_MAX], tail[FILE_MAX]; + ushort numlen; + int framenr = static_cast<NodeTexImage *>(tex_node->storage)->iuser.framenr; + BLI_path_sequence_decode(path, head, tail, &numlen); + BLI_path_sequence_encode(path, head, tail, numlen, framenr); + } + return path; } @@ -141,16 +167,16 @@ static const char *get_image_filepath(const bNode *tex_node) * We only want one that feeds directly into a Material Output node * (that is the behavior of the legacy Python exporter). */ -static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree) +static const bNode *find_bsdf_node(const bNodeTree *nodetree) { if (!nodetree) { return nullptr; } - for (const nodes::NodeRef *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) { - const nodes::InputSocketRef *node_input_socket0 = node->inputs()[0]; - for (const nodes::OutputSocketRef *out_sock : node_input_socket0->directly_linked_sockets()) { - const nodes::NodeRef &in_node = out_sock->node(); - if (in_node.typeinfo()->type == SH_NODE_BSDF_PRINCIPLED) { + for (const bNode *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) { + const bNodeSocket &node_input_socket0 = node->input_socket(0); + for (const bNodeSocket *out_sock : node_input_socket0.directly_linked_sockets()) { + const bNode &in_node = out_sock->owner_node(); + if (in_node.typeinfo->type == SH_NODE_BSDF_PRINCIPLED) { return &in_node; } } @@ -161,58 +187,68 @@ static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree) /** * Store properties found either in bNode or material into r_mtl_mat. */ -static void store_bsdf_properties(const nodes::NodeRef *bsdf_node, +static void store_bsdf_properties(const bNode *bsdf_node, const Material *material, MTLMaterial &r_mtl_mat) { - const bNode *bnode = nullptr; - if (bsdf_node) { - bnode = bsdf_node->bnode(); - } - - /* If p-BSDF is not present, fallback to #Object.Material. */ float roughness = material->roughness; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1}); + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Roughness", {&roughness, 1}); } /* Empirical approximation. Importer should use the inverse of this method. */ float spec_exponent = (1.0f - roughness); spec_exponent *= spec_exponent * 1000.0f; float specular = material->spec; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "Specular", {&specular, 1}); + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Specular", {&specular, 1}); } float metallic = material->metallic; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "Metallic", {&metallic, 1}); + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Metallic", {&metallic, 1}); } float refraction_index = 1.0f; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "IOR", {&refraction_index, 1}); + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "IOR", {&refraction_index, 1}); } - float dissolved = material->a; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "Alpha", {&dissolved, 1}); + float alpha = material->a; + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Alpha", {&alpha, 1}); } - const bool transparent = dissolved != 1.0f; + const bool transparent = alpha != 1.0f; float3 diffuse_col = {material->r, material->g, material->b}; - if (bnode) { - copy_property_from_node(SOCK_RGBA, bnode, "Base Color", {diffuse_col, 3}); + if (bsdf_node) { + copy_property_from_node(SOCK_RGBA, bsdf_node, "Base Color", {diffuse_col, 3}); } float3 emission_col{0.0f}; float emission_strength = 0.0f; - if (bnode) { - copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1}); - copy_property_from_node(SOCK_RGBA, bnode, "Emission", {emission_col, 3}); + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1}); + copy_property_from_node(SOCK_RGBA, bsdf_node, "Emission", {emission_col, 3}); } mul_v3_fl(emission_col, emission_strength); + float sheen = -1.0f; + float clearcoat = -1.0f; + float clearcoat_roughness = -1.0f; + float aniso = -1.0f; + float aniso_rot = -1.0f; + float transmission = -1.0f; + if (bsdf_node) { + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Sheen", {&sheen, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Clearcoat", {&clearcoat, 1}); + copy_property_from_node( + SOCK_FLOAT, bsdf_node, "Clearcoat Roughness", {&clearcoat_roughness, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic", {&aniso, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic Rotation", {&aniso_rot, 1}); + copy_property_from_node(SOCK_FLOAT, bsdf_node, "Transmission", {&transmission, 1}); + } + /* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of `illum`. */ /* Highlight on. */ int illum = 2; @@ -235,26 +271,34 @@ static void store_bsdf_properties(const nodes::NodeRef *bsdf_node, /* Transparency: Glass on, Reflection: Ray trace off */ illum = 9; } - r_mtl_mat.Ns = spec_exponent; + r_mtl_mat.spec_exponent = spec_exponent; if (metallic != 0.0f) { - r_mtl_mat.Ka = {metallic, metallic, metallic}; + r_mtl_mat.ambient_color = {metallic, metallic, metallic}; } else { - r_mtl_mat.Ka = {1.0f, 1.0f, 1.0f}; + r_mtl_mat.ambient_color = {1.0f, 1.0f, 1.0f}; } - r_mtl_mat.Kd = diffuse_col; - r_mtl_mat.Ks = {specular, specular, specular}; - r_mtl_mat.Ke = emission_col; - r_mtl_mat.Ni = refraction_index; - r_mtl_mat.d = dissolved; - r_mtl_mat.illum = illum; + r_mtl_mat.color = diffuse_col; + r_mtl_mat.spec_color = {specular, specular, specular}; + r_mtl_mat.emission_color = emission_col; + r_mtl_mat.ior = refraction_index; + r_mtl_mat.alpha = alpha; + r_mtl_mat.illum_mode = illum; + r_mtl_mat.roughness = roughness; + r_mtl_mat.metallic = metallic; + r_mtl_mat.sheen = sheen; + r_mtl_mat.cc_thickness = clearcoat; + r_mtl_mat.cc_roughness = clearcoat_roughness; + r_mtl_mat.aniso = aniso; + r_mtl_mat.aniso_rot = aniso_rot; + r_mtl_mat.transmit_color = {transmission, transmission, transmission}; } /** * Store image texture options and file-paths in `r_mtl_mat`. */ -static void store_image_textures(const nodes::NodeRef *bsdf_node, - const nodes::NodeTreeRef *node_tree, +static void store_image_textures(const bNode *bsdf_node, + const bNodeTree *node_tree, const Material *material, MTLMaterial &r_mtl_mat) { @@ -262,21 +306,20 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node, /* No nodetree, no images, or no Principled BSDF node. */ return; } - const bNode *bnode = bsdf_node->bnode(); /* Normal Map Texture has two extra tasks of: * - finding a Normal Map node before finding a texture node. * - finding "Strength" property of the node for `-bm` option. */ - for (Map<const eMTLSyntaxElement, tex_map_XX>::MutableItem texture_map : - r_mtl_mat.texture_maps.items()) { - Vector<const nodes::OutputSocketRef *> linked_sockets; + for (int key = 0; key < int(MTLTexMapType::Count); ++key) { + MTLTexMap &value = r_mtl_mat.texture_maps[key]; + Vector<const bNodeSocket *> linked_sockets; const bNode *normal_map_node{nullptr}; - if (texture_map.key == eMTLSyntaxElement::map_Bump) { + if (key == int(MTLTexMapType::Normal)) { /* Find sockets linked to destination "Normal" socket in P-BSDF node. */ - linked_sockets_to_dest_id(bnode, *node_tree, "Normal", linked_sockets); + linked_sockets_to_dest_id(bsdf_node, *node_tree, "Normal", linked_sockets); /* Among the linked sockets, find Normal Map shader node. */ normal_map_node = get_node_of_type(linked_sockets, SH_NODE_NORMAL_MAP); @@ -285,16 +328,17 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node, } else { /* Skip emission map if emission strength is zero. */ - if (texture_map.key == eMTLSyntaxElement::map_Ke) { + if (key == int(MTLTexMapType::Emission)) { float emission_strength = 0.0f; - copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1}); + copy_property_from_node( + SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1}); if (emission_strength == 0.0f) { continue; } } /* Find sockets linked to the destination socket of interest, in P-BSDF node. */ linked_sockets_to_dest_id( - bnode, *node_tree, texture_map.value.dest_socket_id, linked_sockets); + bsdf_node, *node_tree, tex_map_type_to_socket_id[key], linked_sockets); } /* Among the linked sockets, find Image Texture shader node. */ @@ -302,8 +346,8 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node, if (!tex_node) { continue; } - const char *tex_image_filepath = get_image_filepath(tex_node); - if (!tex_image_filepath) { + const std::string tex_image_filepath = get_image_filepath(tex_node); + if (tex_image_filepath.empty()) { continue; } @@ -313,14 +357,14 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node, if (normal_map_node) { copy_property_from_node( - SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.map_Bump_strength, 1}); + SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.normal_strength, 1}); } /* Texture transform options. Only translation (origin offset, "-o") and scale * ("-o") are supported. */ - copy_property_from_node(SOCK_VECTOR, mapping, "Location", {texture_map.value.translation, 3}); - copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {texture_map.value.scale, 3}); + copy_property_from_node(SOCK_VECTOR, mapping, "Location", {value.translation, 3}); + copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {value.scale, 3}); - texture_map.value.image_path = tex_image_filepath; + value.image_path = tex_image_filepath; } } @@ -330,14 +374,14 @@ MTLMaterial mtlmaterial_for_material(const Material *material) MTLMaterial mtlmat; mtlmat.name = std::string(material->id.name + 2); std::replace(mtlmat.name.begin(), mtlmat.name.end(), ' ', '_'); - const nodes::NodeTreeRef *nodetree = nullptr; - if (material->nodetree) { - nodetree = new nodes::NodeTreeRef(material->nodetree); + const bNodeTree *nodetree = material->nodetree; + if (nodetree != nullptr) { + nodetree->ensure_topology_cache(); } - const nodes::NodeRef *bsdf_node = find_bsdf_node(nodetree); + + const bNode *bsdf_node = find_bsdf_node(nodetree); store_bsdf_properties(bsdf_node, material, mtlmat); store_image_textures(bsdf_node, nodetree, material, mtlmat); - delete nodetree; return mtlmat; } |