diff options
Diffstat (limited to 'source/blender/io/wavefront_obj/importer')
8 files changed, 268 insertions, 149 deletions
diff --git a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc index 7019e67419e..f33753d720d 100644 --- a/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc +++ b/source/blender/io/wavefront_obj/importer/importer_mesh_utils.cc @@ -99,13 +99,8 @@ void transform_object(Object *object, const OBJImportParams &import_params) float obmat[4][4]; unit_m4(obmat); /* +Y-forward and +Z-up are the default Blender axis settings. */ - mat3_from_axis_conversion(import_params.forward_axis, - import_params.up_axis, - OBJ_AXIS_Y_FORWARD, - OBJ_AXIS_Z_UP, - axes_transform); - /* mat3_from_axis_conversion returns a transposed matrix! */ - transpose_m3(axes_transform); + mat3_from_axis_conversion( + IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, axes_transform); copy_m4_m3(obmat, axes_transform); BKE_object_apply_mat4(object, obmat, true, false); 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 c7990028312..a32fd90594d 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 @@ -5,12 +5,16 @@ */ #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_import_file_reader.hh" #include "obj_import_string_utils.hh" +#include <charconv> + namespace blender::io::obj { using std::string; @@ -18,31 +22,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(); - 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) { @@ -65,26 +62,85 @@ 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; - parse_floats(p, end, 0.0f, vert, 3); + 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 */ + if (p < end) { + float3 srgb; + p = parse_floats(p, end, -1.0f, srgb, 3); + if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { + float3 linear; + srgb_to_linearrgb_v3_v3(linear, srgb); + + 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(const char *p, const char *end, GlobalVertices &r_global_vertices) +{ + /* MRGB color extension, in the form of + * "#MRGB MMRRGGBBMMRRGGBB ..." + * http://paulbourke.net/dataformats/obj/colour.html */ + p = drop_whitespace(p, end); + const int mrgb_length = 8; + while (p + mrgb_length <= end) { + uint32_t value = 0; + std::from_chars_result res = std::from_chars(p, p + mrgb_length, value, 16); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + return; + } + unsigned char srgb[4]; + srgb[0] = (value >> 16) & 0xFF; + srgb[1] = (value >> 8) & 0xFF; + srgb[2] = value & 0xFF; + srgb[3] = 0xFF; + float linear[4]; + srgb_to_linearrgb_uchar4(linear, srgb); + + 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) @@ -97,24 +153,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) @@ -124,7 +180,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(); @@ -149,12 +205,11 @@ static void geom_add_polygon(Geometry *geom, if (p < end && *p == '/') { ++p; p = parse_int(p, end, INT32_MAX, corner.vertex_normal_index, false); - got_normal = corner.uv_vert_index != INT32_MAX; + got_normal = corner.vertex_normal_index != INT32_MAX; } } /* 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", @@ -162,6 +217,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()) { @@ -172,7 +230,10 @@ static void geom_add_polygon(Geometry *geom, face_valid = false; } } - if (got_normal) { + /* 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 && !global_vertices.vertex_normals.is_empty()) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -206,9 +267,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); @@ -216,8 +275,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; } @@ -343,9 +401,12 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, return; } - VertexIndexOffset offsets; - Geometry *curr_geom = create_geometry( - nullptr, GEOM_MESH, "", r_global_vertices, r_all_geometries, offsets); + /* Use the filename as the default name given to the initial object. */ + char ob_name[FILE_MAXFILE]; + BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE); + BLI_path_extension_replace(ob_name, FILE_MAXFILE, ""); + + 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. */ @@ -368,6 +429,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') { @@ -386,9 +452,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') { @@ -415,10 +479,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); @@ -430,26 +494,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")) { @@ -477,14 +536,16 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, else if (parse_keyword(p, end, "mtllib")) { add_mtl_library(StringRef(p, end).trim()); } + else if (parse_keyword(p, end, "#MRGB")) { + geom_add_mrgb_colors(p, end, r_global_vertices); + } /* Comments. */ else if (*p == '#') { /* Nothing to do. */ } /* 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); @@ -564,15 +625,15 @@ static bool parse_texture_option(const char *&p, { 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->map_Bump_strength, true, true); return true; } if (parse_keyword(p, end, "-type")) { diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 8d560bd2c8c..01f05466b3a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -8,7 +8,9 @@ #include "DNA_mesh_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node_tree_update.h" @@ -29,13 +31,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, Map<std::string, Material *> &created_materials, const OBJImportParams &import_params) { + const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()}; + if (tot_verts_object <= 0) { + /* Empty mesh */ + return nullptr; + } std::string ob_name{mesh_geometry_.geometry_name_}; if (ob_name.empty()) { ob_name = "Untitled"; } fixup_invalid_faces(); - const int64_t tot_verts_object{mesh_geometry_.vertex_count_}; /* Total explicitly imported edges, not the ones belonging the polygons to be created. */ const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()}; @@ -46,10 +52,11 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, obj->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, ob_name.c_str()); create_vertices(mesh); - create_polys_loops(obj, mesh); + create_polys_loops(mesh, import_params.import_vertex_groups); create_edges(mesh); create_uv_verts(mesh); create_normals(mesh); + create_colors(mesh); create_materials(bmain, materials, created_materials, obj); if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) { @@ -67,6 +74,9 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); dst->flag |= autosmooth; + /* NOTE: vertex groups have to be created after final mesh is assigned to the object. */ + create_vertex_groups(obj); + return obj; } @@ -147,9 +157,9 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.vertex_count_}; + const int tot_verts_object{mesh_geometry_.get_vertex_count()}; for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_start_ + i; + int vi = mesh_geometry_.vertex_index_min_ + i; if (vi < global_vertices_.vertices.size()) { copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); } @@ -161,19 +171,13 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) } } -void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) +void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { - /* Will not be used if vertex groups are not imported. */ mesh->dvert = nullptr; - float weight = 0.0f; - const int64_t total_verts = mesh_geometry_.vertex_count_; - if (total_verts && mesh_geometry_.use_vertex_groups_) { + const int64_t total_verts = mesh_geometry_.get_vertex_count(); + if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast<MDeformVert *>( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); - weight = 1.0f / total_verts; - } - else { - UNUSED_VARS(weight); } const int64_t tot_face_elems{mesh->totpoly}; @@ -204,30 +208,25 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; - mloop.v = curr_corner.vert_index; + mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; + /* Setup vertex group data, if needed. */ if (!mesh->dvert) { continue; } - /* Iterating over mloop results in finding the same vertex multiple times. - * Another way is to allocate memory for dvert while creating vertices and fill them here. - */ - MDeformVert &def_vert = mesh->dvert[mloop.v]; - if (!def_vert.dw) { - def_vert.dw = static_cast<MDeformWeight *>( - MEM_callocN(sizeof(MDeformWeight), "OBJ Import Deform Weight")); - } - /* Every vertex in a face is assigned the same deform group. */ - int group_idx = curr_face.vertex_group_index; - /* Deform group number (def_nr) must behave like an index into the names' list. */ - *(def_vert.dw) = {static_cast<unsigned int>(group_idx), weight}; + const int group_index = curr_face.vertex_group_index; + MDeformWeight *dw = BKE_defvert_ensure_index(mesh->dvert + mloop.v, group_index); + dw->weight = 1.0f; } } +} - if (!mesh->dvert) { +void MeshFromGeometry::create_vertex_groups(Object *obj) +{ + Mesh *mesh = static_cast<Mesh *>(obj->data); + if (mesh->dvert == nullptr) { return; } - /* Add deform group names. */ for (const std::string &name : mesh_geometry_.group_order_) { BKE_object_defgroup_add_name(obj, name.data()); } @@ -236,14 +235,14 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh) void MeshFromGeometry::create_edges(Mesh *mesh) { const int64_t tot_edges{mesh_geometry_.edges_.size()}; - const int64_t total_verts{mesh_geometry_.vertex_count_}; + const int64_t total_verts{mesh_geometry_.get_vertex_count()}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts); - dst_edge.v1 = src_edge.v1; - dst_edge.v2 = src_edge.v2; + dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; + dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } @@ -289,7 +288,7 @@ static Material *get_or_create_material(Main *bmain, /* We have not, will have to create it. Create a new default * MTLMaterial too, in case the OBJ file tries to use a material * that was not in the MTL file. */ - const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>()).get(); + const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>()); Material *mat = BKE_material_add(bmain, name.c_str()); ShaderNodetreeWrap mat_wrap{bmain, mtl, mat}; @@ -311,17 +310,14 @@ void MeshFromGeometry::create_materials(Main *bmain, if (mat == nullptr) { continue; } - BKE_object_material_slot_add(bmain, obj); - BKE_object_material_assign(bmain, obj, mat, obj->totcol, BKE_MAT_ASSIGN_USERPREF); + BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1); } } void MeshFromGeometry::create_normals(Mesh *mesh) { - /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */ - /* No normal data: nothing to do. */ - if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) { + if (global_vertices_.vertex_normals.is_empty()) { return; } @@ -345,4 +341,29 @@ void MeshFromGeometry::create_normals(Mesh *mesh) MEM_freeN(loop_normals); } +void MeshFromGeometry::create_colors(Mesh *mesh) +{ + /* Nothing to do if we don't have vertex colors at all. */ + if (global_vertices_.vertex_colors.is_empty()) { + return; + } + + /* Find which vertex color block is for this mesh (if any). */ + for (const auto &block : global_vertices_.vertex_colors) { + if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index && + mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) { + /* This block is suitable, use colors from it. */ + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index; + for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) { + float3 c = block.colors[offset + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } + return; + } + } +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh index cf4a2aee394..591a7b81e63 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh @@ -45,10 +45,9 @@ class MeshFromGeometry : NonMovable, NonCopyable { void fixup_invalid_faces(); void create_vertices(Mesh *mesh); /** - * Create polygons for the Mesh, set smooth shading flags, deform group names, - * Materials. + * Create polygons for the Mesh, set smooth shading flags, Materials. */ - void create_polys_loops(Object *obj, Mesh *mesh); + void create_polys_loops(Mesh *mesh, bool use_vertex_groups); /** * Add explicitly imported OBJ edges to the mesh. */ @@ -65,6 +64,8 @@ class MeshFromGeometry : NonMovable, NonCopyable { Map<std::string, Material *> &created_materials, Object *obj); void create_normals(Mesh *mesh); + void create_colors(Mesh *mesh); + void create_vertex_groups(Object *obj); }; } // namespace blender::io::obj 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..60e419728f3 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc @@ -320,7 +320,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat) if (alpha != -1) { 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; } } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index b67ba46af03..9f0079d7c53 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -19,34 +19,24 @@ namespace blender::io::obj { /** - * List of all vertex and UV vertex coordinates in an OBJ file accessible to any - * Geometry instance at any time. + * All vertex positions, normals, UVs, colors in the OBJ file. */ struct GlobalVertices { Vector<float3> vertices; Vector<float2> uv_vertices; Vector<float3> vertex_normals; -}; - -/** - * Keeps track of the vertices that belong to other Geometries. - * Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total - * vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices - * in the global list). - */ -struct VertexIndexOffset { - private: - int offset_ = 0; - public: - void set_index_offset(const int64_t total_vertices) - { - offset_ = total_vertices; - } - int64_t get_index_offset() const - { - return offset_; - } + /** + * Vertex colors might not be present in the file at all, or only + * provided for some meshes. Store them in chunks as they are + * spelled out in the file, e.g. if there are 10 vertices in sequence, all + * with `xyzrgb` colors, they will be one block. + */ + struct VertexColorsBlock { + Vector<float3> colors; + int start_vertex_index; + }; + Vector<VertexColorsBlock> vertex_colors; }; /** @@ -100,8 +90,8 @@ struct Geometry { Map<std::string, int> material_indices_; Vector<std::string> material_order_; - int vertex_start_ = 0; - int vertex_count_ = 0; + int vertex_index_min_ = INT_MAX; + int vertex_index_max_ = -1; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; @@ -109,10 +99,21 @@ struct Geometry { Vector<PolyElem> face_elements_; bool has_invalid_polys_ = false; - bool has_vertex_normals_ = false; - bool use_vertex_groups_ = false; + bool has_vertex_groups_ = false; NurbsElement nurbs_element_; int total_loops_ = 0; + + int get_vertex_count() const + { + if (vertex_index_max_ < vertex_index_min_) + return 0; + return vertex_index_max_ - vertex_index_min_ + 1; + } + void track_vertex_index(int index) + { + vertex_index_min_ = std::min(vertex_index_min_, index); + vertex_index_max_ = std::max(vertex_index_max_, index); + } }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index c8eaa046e68..9a457167fca 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -18,14 +18,12 @@ StringRef read_next_line(StringRef &buffer) const char *start = buffer.begin(); const char *end = buffer.end(); size_t len = 0; - char prev = 0; const char *ptr = start; while (ptr < end) { char c = *ptr++; - if (c == '\n' && prev != '\\') { + if (c == '\n') { break; } - prev = c; ++len; } @@ -35,7 +33,27 @@ StringRef read_next_line(StringRef &buffer) static bool is_whitespace(char c) { - return c <= ' ' || c == '\\'; + return c <= ' '; +} + +void fixup_line_continuations(char *p, char *end) +{ + while (true) { + /* Find next backslash, if any. */ + char *backslash = std::find(p, end, '\\'); + if (backslash == end) + break; + /* Skip over possible whitespace right after it. */ + p = backslash + 1; + while (p < end && is_whitespace(*p) && *p != '\n') + ++p; + /* If then we have a newline, turn both backslash + * and the newline into regular spaces. */ + if (p < end && *p == '\n') { + *backslash = ' '; + *p = ' '; + } + } } const char *drop_whitespace(const char *p, const char *end) @@ -62,8 +80,12 @@ static const char *drop_plus(const char *p, const char *end) return p; } -const char *parse_float( - const char *p, const char *end, float fallback, float &dst, bool skip_space) +const char *parse_float(const char *p, + const char *end, + float fallback, + float &dst, + bool skip_space, + bool require_trailing_space) { if (skip_space) { p = drop_whitespace(p, end); @@ -73,13 +95,23 @@ const char *parse_float( if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { dst = fallback; } + else if (require_trailing_space && res.ptr < end && !is_whitespace(*res.ptr)) { + /* If there are trailing non-space characters, do not eat up the number. */ + dst = fallback; + return p; + } return res.ptr; } -const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count) +const char *parse_floats(const char *p, + const char *end, + float fallback, + float *dst, + int count, + bool require_trailing_space) { for (int i = 0; i < count; ++i) { - p = parse_float(p, end, fallback, dst[i]); + p = parse_float(p, end, fallback, dst[i], true, require_trailing_space); } return p; } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index 3f428b1ab5c..e42f5080d25 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -6,9 +6,6 @@ /* * Various text parsing utilities used by OBJ importer. - * The utilities are not directly usable by other formats, since - * they treat backslash (\) as a whitespace character (OBJ format - * allows backslashes to function as a line-continuation character). * * Many of these functions take two pointers (p, end) indicating * which part of a string to operate on, and return a possibly @@ -27,21 +24,22 @@ namespace blender::io::obj { * The returned line will not have '\n' characters at the end; * the `buffer` is modified to contain remaining text without * the input line. - * - * Note that backslash (\) character is treated as a line - * continuation. */ StringRef read_next_line(StringRef &buffer); /** + * Fix up OBJ line continuations by replacing backslash (\) and the + * following newline with spaces. + */ +void fixup_line_continuations(char *p, char *end); + +/** * Drop leading white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_whitespace(const char *p, const char *end); /** * Drop leading non-white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_non_whitespace(const char *p, const char *end); @@ -62,12 +60,17 @@ const char *parse_int( * The parsed result is stored in `dst`. The function skips * leading white-space unless `skip_space=false`. If the * number can't be parsed (invalid syntax, out of range), - * `fallback` value is stored instead. + * `fallback` value is stored instead. If `require_trailing_space` + * is true, the character after the number has to be whitespace. * * Returns the start of remainder of the input string after parsing. */ -const char *parse_float( - const char *p, const char *end, float fallback, float &dst, bool skip_space = true); +const char *parse_float(const char *p, + const char *end, + float fallback, + float &dst, + bool skip_space = true, + bool require_trailing_space = false); /** * Parse a number of white-space separated floats from an input string. @@ -77,6 +80,11 @@ const char *parse_float( * * Returns the start of remainder of the input string after parsing. */ -const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count); +const char *parse_floats(const char *p, + const char *end, + float fallback, + float *dst, + int count, + bool require_trailing_space = false); } // namespace blender::io::obj |