diff options
Diffstat (limited to 'source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc')
-rw-r--r-- | source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc | 233 |
1 files changed, 142 insertions, 91 deletions
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index ee55dd1e45a..f92f9894f75 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -6,12 +6,15 @@ #include "BLI_map.hh" #include "BLI_math_color.h" +#include "BLI_math_vector.h" #include "BLI_string_ref.hh" #include "BLI_vector.hh" +#include "obj_export_mtl.hh" #include "obj_import_file_reader.hh" #include "obj_import_string_utils.hh" +#include <algorithm> #include <charconv> namespace blender::io::obj { @@ -21,32 +24,24 @@ using std::string; /** * Based on the properties of the given Geometry instance, create a new Geometry instance * or return the previous one. - * - * Also update index offsets which should always happen if a new Geometry instance is created. */ static Geometry *create_geometry(Geometry *const prev_geometry, const eGeometryType new_type, StringRef name, - const GlobalVertices &global_vertices, - Vector<std::unique_ptr<Geometry>> &r_all_geometries, - VertexIndexOffset &r_offset) + Vector<std::unique_ptr<Geometry>> &r_all_geometries) { auto new_geometry = [&]() { r_all_geometries.append(std::make_unique<Geometry>()); Geometry *g = r_all_geometries.last().get(); g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; - g->vertex_start_ = global_vertices.vertices.size(); - g->vertex_color_start_ = global_vertices.vertex_colors.size(); - r_offset.set_index_offset(g->vertex_start_); return g; }; if (prev_geometry && prev_geometry->geom_type_ == GEOM_MESH) { /* After the creation of a Geometry instance, at least one element has been found in the OBJ - * file that indicates that it is a mesh (basically anything but the vertex positions). */ - if (!prev_geometry->face_elements_.is_empty() || prev_geometry->has_vertex_normals_ || - !prev_geometry->edges_.is_empty()) { + * file that indicates that it is a mesh (faces or edges). */ + if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) { return new_geometry(); } if (new_type == GEOM_MESH) { @@ -69,15 +64,11 @@ static Geometry *create_geometry(Geometry *const prev_geometry, return new_geometry(); } -static void geom_add_vertex(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) { float3 vert; p = parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); - geom->vertex_count_++; /* OBJ extension: `xyzrgb` vertex colors, when the vertex position * is followed by 3 more RGB color components. See * http://paulbourke.net/dataformats/obj/colour.html */ @@ -87,16 +78,22 @@ static void geom_add_vertex(Geometry *geom, if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { float3 linear; srgb_to_linearrgb_v3_v3(linear, srgb); - r_global_vertices.vertex_colors.append(linear); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size() - 1)) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size() - 1; + blocks.append(block); + } + blocks.last().colors.append(linear); } } } -static void geom_add_mrgb_colors(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_mrgb_colors(const char *p, const char *end, GlobalVertices &r_global_vertices) { /* MRGB color extension, in the form of * "#MRGB MMRRGGBBMMRRGGBB ..." @@ -116,21 +113,36 @@ static void geom_add_mrgb_colors(Geometry *geom, srgb[3] = 0xFF; float linear[4]; srgb_to_linearrgb_uchar4(linear, srgb); - r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]}); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size())) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size(); + blocks.append(block); + } + blocks.last().colors.append({linear[0], linear[1], linear[2]}); + /* MRGB colors are specified after vertex positions; each new color + * "pushes" the vertex colors block further back into which vertices it is for. */ + blocks.last().start_vertex_index--; + p += mrgb_length; } } -static void geom_add_vertex_normal(Geometry *geom, - const char *p, +static void geom_add_vertex_normal(const char *p, const char *end, GlobalVertices &r_global_vertices) { float3 normal; parse_floats(p, end, 0.0f, normal, 3); + /* Normals can be printed with only several digits in the file, + * making them ever-so-slightly non unit length. Make sure they are + * normalized. */ + normalize_v3(normal); r_global_vertices.vertex_normals.append(normal); - geom->has_vertex_normals_ = true; } static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) @@ -143,24 +155,24 @@ static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r static void geom_add_edge(Geometry *geom, const char *p, const char *end, - const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { int edge_v1, edge_v2; p = parse_int(p, end, -1, edge_v1); p = parse_int(p, end, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ - edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; - edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; + edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -1; + edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -1; BLI_assert(edge_v1 >= 0 && edge_v2 >= 0); geom->edges_.append({static_cast<uint>(edge_v1), static_cast<uint>(edge_v2)}); + geom->track_vertex_index(edge_v1); + geom->track_vertex_index(edge_v2); } static void geom_add_polygon(Geometry *geom, const char *p, const char *end, const GlobalVertices &global_vertices, - const VertexIndexOffset &offsets, const int material_index, const int group_index, const bool shaded_smooth) @@ -170,7 +182,7 @@ static void geom_add_polygon(Geometry *geom, curr_face.material_index = material_index; if (group_index >= 0) { curr_face.vertex_group_index = group_index; - geom->use_vertex_groups_ = true; + geom->has_vertex_groups_ = true; } const int orig_corners_size = geom->face_corners_.size(); @@ -199,8 +211,7 @@ static void geom_add_polygon(Geometry *geom, } } /* Always keep stored indices non-negative and zero-based. */ - corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : - -offsets.get_index_offset() - 1; + corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -1; if (corner.vert_index < 0 || corner.vert_index >= global_vertices.vertices.size()) { fprintf(stderr, "Invalid vertex index %i (valid range [0, %zu)), ignoring face\n", @@ -208,6 +219,9 @@ static void geom_add_polygon(Geometry *geom, (size_t)global_vertices.vertices.size()); face_valid = false; } + else { + geom->track_vertex_index(corner.vert_index); + } if (got_uv) { corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1; if (corner.uv_vert_index < 0 || corner.uv_vert_index >= global_vertices.uv_vertices.size()) { @@ -221,7 +235,7 @@ static void geom_add_polygon(Geometry *geom, /* Ignore corner normal index, if the geometry does not have any normals. * Some obj files out there do have face definitions that refer to normal indices, * without any normals being present (T98782). */ - if (got_normal && geom->has_vertex_normals_) { + if (got_normal && !global_vertices.vertex_normals.is_empty()) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -255,9 +269,7 @@ static void geom_add_polygon(Geometry *geom, static Geometry *geom_set_curve_type(Geometry *geom, const char *p, const char *end, - const GlobalVertices &global_vertices, const StringRef group_name, - VertexIndexOffset &r_offsets, Vector<std::unique_ptr<Geometry>> &r_all_geometries) { p = drop_whitespace(p, end); @@ -265,8 +277,7 @@ static Geometry *geom_set_curve_type(Geometry *geom, std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl; return geom; } - geom = create_geometry( - geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets); + geom = create_geometry(geom, GEOM_CURVE, group_name, r_all_geometries); geom->nurbs_element_.group_ = group_name; return geom; } @@ -385,6 +396,22 @@ static bool parse_keyword(const char *&p, const char *end, StringRef keyword) return true; } +/* Special case: if there were no faces/edges in any geometries, + * treat all the vertices as a point cloud. */ +static void use_all_vertices_if_no_faces(Geometry *geom, + const Vector<std::unique_ptr<Geometry>> &all_geometries, + const GlobalVertices &global_vertices) +{ + if (!global_vertices.vertices.is_empty() && geom && geom->geom_type_ == GEOM_MESH) { + if (std::all_of( + all_geometries.begin(), all_geometries.end(), [](const std::unique_ptr<Geometry> &g) { + return g->get_vertex_count() == 0; + })) { + geom->track_all_vertices(global_vertices.vertices.size()); + } + } +} + void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, GlobalVertices &r_global_vertices) { @@ -397,9 +424,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE); BLI_path_extension_replace(ob_name, FILE_MAXFILE, ""); - VertexIndexOffset offsets; - Geometry *curr_geom = create_geometry( - nullptr, GEOM_MESH, ob_name, r_global_vertices, r_all_geometries, offsets); + Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries); /* State variables: once set, they remain the same for the remaining * elements in the object. */ @@ -422,6 +447,11 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, break; /* No more data to read. */ } + /* Take care of line continuations now (turn them into spaces); + * the rest of the parsing code does not need to worry about them anymore. */ + fixup_line_continuations(buffer.data() + buffer_offset, + buffer.data() + buffer_offset + bytes_read); + /* Ensure buffer ends in a newline. */ if (bytes_read < read_buffer_size_) { if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') { @@ -440,9 +470,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, while (last_nl > 0) { --last_nl; if (buffer[last_nl] == '\n') { - if (last_nl < 1 || buffer[last_nl - 1] != '\\') { - break; - } + break; } } if (buffer[last_nl] != '\n') { @@ -469,10 +497,10 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, /* Most common things that start with 'v': vertices, normals, UVs. */ if (*p == 'v') { if (parse_keyword(p, end, "v")) { - geom_add_vertex(curr_geom, p, end, r_global_vertices); + geom_add_vertex(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vn")) { - geom_add_vertex_normal(curr_geom, p, end, r_global_vertices); + geom_add_vertex_normal(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vt")) { geom_add_uv_vertex(p, end, r_global_vertices); @@ -484,26 +512,21 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, p, end, r_global_vertices, - offsets, state_material_index, - state_group_index, /* TODO was wrongly material name! */ + state_group_index, state_shaded_smooth); } /* Faces. */ else if (parse_keyword(p, end, "l")) { - geom_add_edge(curr_geom, p, end, offsets, r_global_vertices); + geom_add_edge(curr_geom, p, end, r_global_vertices); } /* Objects. */ else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; state_material_name = ""; - curr_geom = create_geometry(curr_geom, - GEOM_MESH, - StringRef(p, end).trim(), - r_global_vertices, - r_all_geometries, - offsets); + curr_geom = create_geometry( + curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries); } /* Groups. */ else if (parse_keyword(p, end, "g")) { @@ -532,7 +555,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, add_mtl_library(StringRef(p, end).trim()); } else if (parse_keyword(p, end, "#MRGB")) { - geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices); + geom_add_mrgb_colors(p, end, r_global_vertices); } /* Comments. */ else if (*p == '#') { @@ -540,8 +563,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, } /* Curve related things. */ else if (parse_keyword(p, end, "cstype")) { - curr_geom = geom_set_curve_type( - curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries); + curr_geom = geom_set_curve_type(curr_geom, p, end, state_group_name, r_all_geometries); } else if (parse_keyword(p, end, "deg")) { geom_set_curve_degree(curr_geom, p, end); @@ -567,39 +589,44 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, buffer_offset = left_size; } + use_all_vertices_if_no_faces(curr_geom, r_all_geometries, r_global_vertices); add_default_mtl_library(); } -static eMTLSyntaxElement mtl_line_start_to_enum(const char *&p, const char *end) +static MTLTexMapType mtl_line_start_to_texture_type(const char *&p, const char *end) { if (parse_keyword(p, end, "map_Kd")) { - return eMTLSyntaxElement::map_Kd; + return MTLTexMapType::Color; } if (parse_keyword(p, end, "map_Ks")) { - return eMTLSyntaxElement::map_Ks; + return MTLTexMapType::Specular; } if (parse_keyword(p, end, "map_Ns")) { - return eMTLSyntaxElement::map_Ns; + return MTLTexMapType::SpecularExponent; } if (parse_keyword(p, end, "map_d")) { - return eMTLSyntaxElement::map_d; + return MTLTexMapType::Alpha; } - if (parse_keyword(p, end, "refl")) { - return eMTLSyntaxElement::map_refl; - } - if (parse_keyword(p, end, "map_refl")) { - return eMTLSyntaxElement::map_refl; + if (parse_keyword(p, end, "refl") || parse_keyword(p, end, "map_refl")) { + return MTLTexMapType::Reflection; } if (parse_keyword(p, end, "map_Ke")) { - return eMTLSyntaxElement::map_Ke; + return MTLTexMapType::Emission; + } + if (parse_keyword(p, end, "bump") || parse_keyword(p, end, "map_Bump") || + parse_keyword(p, end, "map_bump")) { + return MTLTexMapType::Normal; } - if (parse_keyword(p, end, "bump")) { - return eMTLSyntaxElement::map_Bump; + if (parse_keyword(p, end, "map_Pr")) { + return MTLTexMapType::Roughness; } - if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) { - return eMTLSyntaxElement::map_Bump; + if (parse_keyword(p, end, "map_Pm")) { + return MTLTexMapType::Metallic; } - return eMTLSyntaxElement::string; + if (parse_keyword(p, end, "map_Ps")) { + return MTLTexMapType::Sheen; + } + return MTLTexMapType::Count; } static const std::pair<StringRef, int> unsupported_texture_options[] = { @@ -617,19 +644,19 @@ static const std::pair<StringRef, int> unsupported_texture_options[] = { static bool parse_texture_option(const char *&p, const char *end, MTLMaterial *material, - tex_map_XX &tex_map) + MTLTexMap &tex_map) { p = drop_whitespace(p, end); if (parse_keyword(p, end, "-o")) { - p = parse_floats(p, end, 0.0f, tex_map.translation, 3); + p = parse_floats(p, end, 0.0f, tex_map.translation, 3, true); return true; } if (parse_keyword(p, end, "-s")) { - p = parse_floats(p, end, 1.0f, tex_map.scale, 3); + p = parse_floats(p, end, 1.0f, tex_map.scale, 3, true); return true; } if (parse_keyword(p, end, "-bm")) { - p = parse_float(p, end, 1.0f, material->map_Bump_strength); + p = parse_float(p, end, 1.0f, material->normal_strength, true, true); return true; } if (parse_keyword(p, end, "-type")) { @@ -671,13 +698,13 @@ static void parse_texture_map(const char *p, if (!is_map && !is_refl && !is_bump) { return; } - eMTLSyntaxElement key = mtl_line_start_to_enum(p, end); - if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) { + MTLTexMapType key = mtl_line_start_to_texture_type(p, end); + if (key == MTLTexMapType::Count) { /* No supported texture map found. */ std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl; return; } - tex_map_XX &tex_map = material->texture_maps.lookup(key); + MTLTexMap &tex_map = material->tex_map_of_type(key); tex_map.mtl_dir_path = mtl_dir_path; /* Parse texture map options. */ @@ -726,7 +753,7 @@ MTLParser::MTLParser(StringRefNull mtl_library, StringRefNull obj_filepath) { char obj_file_dir[FILE_MAXDIR]; BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR); - BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL); + BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), nullptr); BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR); } @@ -762,31 +789,55 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat } else if (material != nullptr) { if (parse_keyword(p, end, "Ns")) { - parse_float(p, end, 324.0f, material->Ns); + parse_float(p, end, 324.0f, material->spec_exponent); } else if (parse_keyword(p, end, "Ka")) { - parse_floats(p, end, 0.0f, material->Ka, 3); + parse_floats(p, end, 0.0f, material->ambient_color, 3); } else if (parse_keyword(p, end, "Kd")) { - parse_floats(p, end, 0.8f, material->Kd, 3); + parse_floats(p, end, 0.8f, material->color, 3); } else if (parse_keyword(p, end, "Ks")) { - parse_floats(p, end, 0.5f, material->Ks, 3); + parse_floats(p, end, 0.5f, material->spec_color, 3); } else if (parse_keyword(p, end, "Ke")) { - parse_floats(p, end, 0.0f, material->Ke, 3); + parse_floats(p, end, 0.0f, material->emission_color, 3); } else if (parse_keyword(p, end, "Ni")) { - parse_float(p, end, 1.45f, material->Ni); + parse_float(p, end, 1.45f, material->ior); } else if (parse_keyword(p, end, "d")) { - parse_float(p, end, 1.0f, material->d); + parse_float(p, end, 1.0f, material->alpha); } else if (parse_keyword(p, end, "illum")) { /* Some files incorrectly use a float (T60135). */ float val; parse_float(p, end, 1.0f, val); - material->illum = val; + material->illum_mode = val; + } + else if (parse_keyword(p, end, "Pr")) { + parse_float(p, end, 0.5f, material->roughness); + } + else if (parse_keyword(p, end, "Pm")) { + parse_float(p, end, 0.0f, material->metallic); + } + else if (parse_keyword(p, end, "Ps")) { + parse_float(p, end, 0.0f, material->sheen); + } + else if (parse_keyword(p, end, "Pc")) { + parse_float(p, end, 0.0f, material->cc_thickness); + } + else if (parse_keyword(p, end, "Pcr")) { + parse_float(p, end, 0.0f, material->cc_roughness); + } + else if (parse_keyword(p, end, "aniso")) { + parse_float(p, end, 0.0f, material->aniso); + } + else if (parse_keyword(p, end, "anisor")) { + parse_float(p, end, 0.0f, material->aniso_rot); + } + else if (parse_keyword(p, end, "Kt") || parse_keyword(p, end, "Tf")) { + parse_floats(p, end, 0.0f, material->transmit_color, 3); } else { parse_texture_map(p, end, material, mtl_dir_path_); |