diff options
Diffstat (limited to 'source/blender/io/wavefront_obj/importer/obj_import_mtl.cc')
-rw-r--r-- | source/blender/io/wavefront_obj/importer/obj_import_mtl.cc | 301 |
1 files changed, 161 insertions, 140 deletions
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc index f39def0a4af..0922a71979e 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -5,18 +5,19 @@ */ #include "BKE_image.h" +#include "BKE_main.h" #include "BKE_node.h" #include "BLI_map.hh" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "NOD_shader.h" -/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */ -#include "obj_export_io.hh" +#include "obj_export_mtl.hh" #include "obj_import_mtl.hh" #include "obj_import_string_utils.hh" @@ -27,12 +28,12 @@ namespace blender::io::obj { * Only float value(s) can be set using this method. */ static void set_property_of_socket(eNodeSocketDatatype property_type, - StringRef socket_id, + const char *socket_id, Span<float> value, bNode *r_node) { BLI_assert(r_node); - bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id.data())}; + bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id)}; BLI_assert(socket && socket->type == property_type); switch (property_type) { case SOCK_FLOAT: { @@ -60,136 +61,124 @@ static void set_property_of_socket(eNodeSocketDatatype property_type, } } -static bool load_texture_image_at_path(Main *bmain, - const tex_map_XX &tex_map, - bNode *r_node, - const std::string &path) +static Image *load_image_at_path(Main *bmain, const std::string &path, bool relative_paths) { - Image *tex_image = BKE_image_load(bmain, path.c_str()); - if (!tex_image) { + Image *image = BKE_image_load_exists(bmain, path.c_str()); + if (!image) { fprintf(stderr, "Cannot load image file: '%s'\n", path.c_str()); - return false; + return nullptr; } fprintf(stderr, "Loaded image from: '%s'\n", path.c_str()); - r_node->id = reinterpret_cast<ID *>(tex_image); - NodeTexImage *image = static_cast<NodeTexImage *>(r_node->storage); - image->projection = tex_map.projection_type; - return true; + if (relative_paths) { + BLI_path_rel(image->filepath, BKE_main_blendfile_path(bmain)); + } + return image; } -/** - * Load image for Image Texture node and set the node properties. - * Return success if Image can be loaded successfully. - */ -static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_node) +static Image *create_placeholder_image(Main *bmain, const std::string &path) +{ + const float color[4] = {0, 0, 0, 1}; + Image *image = BKE_image_add_generated(bmain, + 32, + 32, + BLI_path_basename(path.c_str()), + 24, + false, + IMA_GENTYPE_BLANK, + color, + false, + false, + false); + STRNCPY(image->filepath, path.c_str()); + image->source = IMA_SRC_FILE; + return image; +} + +static Image *load_texture_image(Main *bmain, const MTLTexMap &tex_map, bool relative_paths) { - BLI_assert(r_node && r_node->type == SH_NODE_TEX_IMAGE); + Image *image = nullptr; /* First try treating texture path as relative. */ std::string tex_path{tex_map.mtl_dir_path + tex_map.image_path}; - if (load_texture_image_at_path(bmain, tex_map, r_node, tex_path)) { - return true; + image = load_image_at_path(bmain, tex_path, relative_paths); + if (image != nullptr) { + return image; } /* Then try using it directly as absolute path. */ std::string raw_path{tex_map.image_path}; - if (load_texture_image_at_path(bmain, tex_map, r_node, raw_path)) { - return true; + image = load_image_at_path(bmain, raw_path, relative_paths); + if (image != nullptr) { + return image; } /* Try removing quotes. */ std::string no_quote_path{tex_path}; auto end_pos = std::remove(no_quote_path.begin(), no_quote_path.end(), '"'); no_quote_path.erase(end_pos, no_quote_path.end()); - if (no_quote_path != tex_path && - load_texture_image_at_path(bmain, tex_map, r_node, no_quote_path)) { - return true; + if (no_quote_path != tex_path) { + image = load_image_at_path(bmain, no_quote_path, relative_paths); + if (image != nullptr) { + return image; + } } /* Try replacing underscores with spaces. */ std::string no_underscore_path{no_quote_path}; std::replace(no_underscore_path.begin(), no_underscore_path.end(), '_', ' '); - if (no_underscore_path != no_quote_path && no_underscore_path != tex_path && - load_texture_image_at_path(bmain, tex_map, r_node, no_underscore_path)) { - return true; + if (no_underscore_path != no_quote_path && no_underscore_path != tex_path) { + image = load_image_at_path(bmain, no_underscore_path, relative_paths); + if (image != nullptr) { + return image; + } } - - return false; -} - -ShaderNodetreeWrap::ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat, Material *mat) - : mtl_mat_(mtl_mat) -{ - nodetree_.reset(ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname)); - bsdf_ = add_node_to_tree(SH_NODE_BSDF_PRINCIPLED); - shader_output_ = add_node_to_tree(SH_NODE_OUTPUT_MATERIAL); - - set_bsdf_socket_values(mat); - add_image_textures(bmain, mat); - link_sockets(bsdf_, "BSDF", shader_output_, "Surface", 4); - - nodeSetActive(nodetree_.get(), shader_output_); -} - -/** - * Assert if caller hasn't acquired nodetree. - */ -ShaderNodetreeWrap::~ShaderNodetreeWrap() -{ - if (nodetree_) { - /* nodetree's ownership must be acquired by the caller. */ - nodetree_.reset(); - BLI_assert(0); + /* Try taking just the basename from input path. */ + std::string base_path{tex_map.mtl_dir_path + BLI_path_basename(tex_map.image_path.c_str())}; + if (base_path != tex_path) { + image = load_image_at_path(bmain, base_path, relative_paths); + if (image != nullptr) { + return image; + } } -} -bNodeTree *ShaderNodetreeWrap::get_nodetree() -{ - /* If this function has been reached, we know that nodes and the nodetree - * can be added to the scene safely. */ - return nodetree_.release(); + image = create_placeholder_image(bmain, tex_path); + return image; } -bNode *ShaderNodetreeWrap::add_node_to_tree(const int node_type) -{ - return nodeAddStaticNode(nullptr, nodetree_.get(), node_type); -} +/* Nodes are arranged in columns by type, with manually placed x coordinates + * based on node widths. */ +const float node_locx_texcoord = -880.0f; +const float node_locx_mapping = -680.0f; +const float node_locx_image = -480.0f; +const float node_locx_normalmap = -200.0f; +const float node_locx_bsdf = 0.0f; +const float node_locx_output = 280.0f; -std::pair<float, float> ShaderNodetreeWrap::set_node_locations(const int pos_x) +/* Nodes are arranged in rows; one row for each image being used. */ +const float node_locy_top = 300.0f; +const float node_locy_step = 300.0f; + +/* Add a node of the given type at the given location. */ +static bNode *add_node(bNodeTree *ntree, int type, float x, float y) { - int pos_y = 0; - bool found = false; - while (true) { - for (Span<int> location : node_locations) { - if (location[0] == pos_x && location[1] == pos_y) { - pos_y += 1; - found = true; - } - else { - found = false; - } - } - if (!found) { - node_locations.append({pos_x, pos_y}); - return {pos_x * node_size_, pos_y * node_size_ * 2.0 / 3.0}; - } - } + bNode *node = nodeAddStaticNode(nullptr, ntree, type); + node->locx = x; + node->locy = y; + return node; } -void ShaderNodetreeWrap::link_sockets(bNode *from_node, - StringRef from_node_id, - bNode *to_node, - StringRef to_node_id, - const int from_node_pos_x) +static void link_sockets(bNodeTree *ntree, + bNode *from_node, + const char *from_node_id, + bNode *to_node, + const char *to_node_id) { - std::tie(from_node->locx, from_node->locy) = set_node_locations(from_node_pos_x); - std::tie(to_node->locx, to_node->locy) = set_node_locations(from_node_pos_x + 1); - bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id.data())}; - bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id.data())}; + bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id)}; + bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id)}; BLI_assert(from_sock && to_sock); - nodeAddLink(nodetree_.get(), from_node, from_sock, to_node, to_sock); + nodeAddLink(ntree, from_node, from_sock, to_node, to_sock); } -void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) +static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat) { - const int illum = mtl_mat_.illum; + const int illum = mtl_mat.illum; bool do_highlight = false; bool do_tranparency = false; bool do_reflection = false; @@ -255,21 +244,21 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) /* Approximations for trying to map obj/mtl material model into * Principled BSDF: */ /* Specular: average of Ks components. */ - float specular = (mtl_mat_.Ks[0] + mtl_mat_.Ks[1] + mtl_mat_.Ks[2]) / 3; + float specular = (mtl_mat.Ks[0] + mtl_mat.Ks[1] + mtl_mat.Ks[2]) / 3; if (specular < 0.0f) { specular = do_highlight ? 1.0f : 0.0f; } /* Roughness: map 0..1000 range to 1..0 and apply non-linearity. */ float roughness; - if (mtl_mat_.Ns < 0.0f) { + if (mtl_mat.Ns < 0.0f) { roughness = do_highlight ? 0.0f : 1.0f; } else { - float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat_.Ns)); + float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat.Ns)); roughness = 1.0f - sqrt(clamped_ns / 1000.0f); } /* Metallic: average of Ka components. */ - float metallic = (mtl_mat_.Ka[0] + mtl_mat_.Ka[1] + mtl_mat_.Ka[2]) / 3; + float metallic = (mtl_mat.Ka[0] + mtl_mat.Ka[1] + mtl_mat.Ka[2]) / 3; if (do_reflection) { if (metallic < 0.0f) { metallic = 1.0f; @@ -279,7 +268,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) metallic = 0.0f; } - float ior = mtl_mat_.Ni; + float ior = mtl_mat.Ni; if (ior < 0) { if (do_tranparency) { ior = 1.0f; @@ -288,89 +277,121 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) ior = 1.5f; } } - float alpha = mtl_mat_.d; + float alpha = mtl_mat.d; if (do_tranparency && alpha < 0) { alpha = 1.0f; } - float3 base_color = {mtl_mat_.Kd[0], mtl_mat_.Kd[1], mtl_mat_.Kd[2]}; + float3 base_color = {mtl_mat.Kd[0], mtl_mat.Kd[1], mtl_mat.Kd[2]}; if (base_color.x >= 0 && base_color.y >= 0 && base_color.z >= 0) { - set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf_); + set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf); /* Viewport shading uses legacy r,g,b base color. */ mat->r = base_color.x; mat->g = base_color.y; mat->b = base_color.z; } - float3 emission_color = {mtl_mat_.Ke[0], mtl_mat_.Ke[1], mtl_mat_.Ke[2]}; + float3 emission_color = {mtl_mat.Ke[0], mtl_mat.Ke[1], mtl_mat.Ke[2]}; if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) { - set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_); + set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf); } - if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) { - set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_); + if (mtl_mat.tex_map_of_type(MTLTexMapType::Ke).is_valid()) { + set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf); } - set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_); - set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf_); + set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf); + set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf); mat->roughness = roughness; - set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf_); + set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf); mat->metallic = metallic; if (ior != -1) { - set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf_); + set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf); } if (alpha != -1) { - set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf_); + set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf); } - if (do_tranparency) { + if (do_tranparency || (alpha >= 0.0f && alpha < 1.0f)) { mat->blend_method = MA_BM_BLEND; } } -void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat) +static void add_image_textures(Main *bmain, + bNodeTree *ntree, + bNode *bsdf, + Material *mat, + const MTLMaterial &mtl_mat, + bool relative_paths) { - for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map : - mtl_mat_.texture_maps.items()) { - if (texture_map.value.image_path.empty()) { + float node_locy = node_locy_top; + for (int key = 0; key < (int)MTLTexMapType::Count; ++key) { + const MTLTexMap &value = mtl_mat.texture_maps[key]; + if (!value.is_valid()) { /* No Image texture node of this map type can be added to this material. */ continue; } - bNode *image_texture = add_node_to_tree(SH_NODE_TEX_IMAGE); - if (!load_texture_image(bmain, texture_map.value, image_texture)) { - /* Image could not be added, so don't add or link further nodes. */ + Image *image = load_texture_image(bmain, value, relative_paths); + if (image == nullptr) { continue; } + bNode *image_node = add_node(ntree, SH_NODE_TEX_IMAGE, node_locx_image, node_locy); + BLI_assert(image_node); + image_node->id = &image->id; + static_cast<NodeTexImage *>(image_node->storage)->projection = value.projection_type; + /* Add normal map node if needed. */ bNode *normal_map = nullptr; - if (texture_map.key == eMTLSyntaxElement::map_Bump) { - normal_map = add_node_to_tree(SH_NODE_NORMAL_MAP); - const float bump = std::max(0.0f, mtl_mat_.map_Bump_strength); + if (key == (int)MTLTexMapType::bump) { + normal_map = add_node(ntree, SH_NODE_NORMAL_MAP, node_locx_normalmap, node_locy); + const float bump = std::max(0.0f, mtl_mat.map_Bump_strength); set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map); } /* Add UV mapping & coordinate nodes only if needed. */ - if (texture_map.value.translation != float3(0, 0, 0) || - texture_map.value.scale != float3(1, 1, 1)) { - bNode *mapping = add_node_to_tree(SH_NODE_MAPPING); - bNode *texture_coordinate = add_node_to_tree(SH_NODE_TEX_COORD); - set_property_of_socket(SOCK_VECTOR, "Location", {texture_map.value.translation, 3}, mapping); - set_property_of_socket(SOCK_VECTOR, "Scale", {texture_map.value.scale, 3}, mapping); - - link_sockets(texture_coordinate, "UV", mapping, "Vector", 0); - link_sockets(mapping, "Vector", image_texture, "Vector", 1); + if (value.translation != float3(0, 0, 0) || value.scale != float3(1, 1, 1)) { + bNode *texcoord = add_node(ntree, SH_NODE_TEX_COORD, node_locx_texcoord, node_locy); + bNode *mapping = add_node(ntree, SH_NODE_MAPPING, node_locx_mapping, node_locy); + set_property_of_socket(SOCK_VECTOR, "Location", {value.translation, 3}, mapping); + set_property_of_socket(SOCK_VECTOR, "Scale", {value.scale, 3}, mapping); + + link_sockets(ntree, texcoord, "UV", mapping, "Vector"); + link_sockets(ntree, mapping, "Vector", image_node, "Vector"); } if (normal_map) { - link_sockets(image_texture, "Color", normal_map, "Color", 2); - link_sockets(normal_map, "Normal", bsdf_, "Normal", 3); + link_sockets(ntree, image_node, "Color", normal_map, "Color"); + link_sockets(ntree, normal_map, "Normal", bsdf, "Normal"); } - else if (texture_map.key == eMTLSyntaxElement::map_d) { - link_sockets(image_texture, "Alpha", bsdf_, texture_map.value.dest_socket_id, 2); + else if (key == (int)MTLTexMapType::d) { + link_sockets(ntree, image_node, "Alpha", bsdf, tex_map_type_to_socket_id[key]); mat->blend_method = MA_BM_BLEND; } else { - link_sockets(image_texture, "Color", bsdf_, texture_map.value.dest_socket_id, 2); + link_sockets(ntree, image_node, "Color", bsdf, tex_map_type_to_socket_id[key]); } + + /* Next layout row: goes downwards on the screen. */ + node_locy -= node_locy_step; } } + +bNodeTree *create_mtl_node_tree(Main *bmain, + const MTLMaterial &mtl, + Material *mat, + bool relative_paths) +{ + bNodeTree *ntree = ntreeAddTreeEmbedded( + nullptr, &mat->id, "Shader Nodetree", ntreeType_Shader->idname); + + bNode *bsdf = add_node(ntree, SH_NODE_BSDF_PRINCIPLED, node_locx_bsdf, node_locy_top); + bNode *output = add_node(ntree, SH_NODE_OUTPUT_MATERIAL, node_locx_output, node_locy_top); + + set_bsdf_socket_values(bsdf, mat, mtl); + add_image_textures(bmain, ntree, bsdf, mat, mtl, relative_paths); + link_sockets(ntree, bsdf, "BSDF", output, "Surface"); + nodeSetActive(ntree, output); + + return ntree; +} + } // namespace blender::io::obj |