Changeset View
Standalone View
source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* If line starts with keyword followed by whitespace, returns true and drops it from the line. */ | |||||
| static bool parse_keyword(StringRef &line, StringRef keyword) | |||||
| { | |||||
| const size_t keyword_len = keyword.size(); | |||||
| if (line.size() < keyword_len + 1) { | |||||
| return false; | |||||
| } | |||||
| if (!line.startswith(keyword)) { | |||||
| return false; | |||||
| } | |||||
| if (line[keyword_len] > ' ') { | |||||
| return false; | |||||
howardt: I guess this is just allowing any control character as "whitespace". This is OK but a little… | |||||
aras_pAuthorUnsubmitted Done Inline Actions
Yes, isspace() has a massive overhead (checks locale, which involves at least a TLS access and possibly a mutex lock etc.). I'll add a comment clarifying this.
No, the first thing the function does is check the length for "keyword length plus at least one more character".
The rest of parsing code generally whitespace before other things, so that's fine. This one needs to basically check "is there a keyword _and_ whitespace after it", since some keywords have the same prefixes, e.g. can't just check for v without checking what's after that, since it could be vn or vt. aras_p: > I guess this is just allowing any control character as "whitespace". This is OK but a little… | |||||
| } | |||||
| line = line.drop_prefix(keyword_len + 1); | |||||
| return true; | |||||
| } | |||||
| void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, | void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, | ||||
| GlobalVertices &r_global_vertices) | GlobalVertices &r_global_vertices) | ||||
| { | { | ||||
| Context not available. | |||||
| } | } | ||||
| /* Most common things that start with 'v': vertices, normals, UVs. */ | /* Most common things that start with 'v': vertices, normals, UVs. */ | ||||
| if (line[0] == 'v') { | if (line[0] == 'v') { | ||||
| if (line.startswith("v ")) { | if (parse_keyword(line, "v")) { | ||||
| line = line.drop_prefix(2); | |||||
| geom_add_vertex(curr_geom, line, r_global_vertices); | geom_add_vertex(curr_geom, line, r_global_vertices); | ||||
| } | } | ||||
| else if (line.startswith("vn ")) { | else if (parse_keyword(line, "vn")) { | ||||
| line = line.drop_prefix(3); | |||||
| geom_add_vertex_normal(curr_geom, line, r_global_vertices); | geom_add_vertex_normal(curr_geom, line, r_global_vertices); | ||||
| } | } | ||||
| else if (line.startswith("vt ")) { | else if (parse_keyword(line, "vt")) { | ||||
| line = line.drop_prefix(3); | |||||
| geom_add_uv_vertex(line, r_global_vertices); | geom_add_uv_vertex(line, r_global_vertices); | ||||
| } | } | ||||
| } | } | ||||
| /* Faces. */ | /* Faces. */ | ||||
| else if (line.startswith("f ")) { | else if (parse_keyword(line, "f")) { | ||||
| line = line.drop_prefix(2); | |||||
| geom_add_polygon(curr_geom, | geom_add_polygon(curr_geom, | ||||
| line, | line, | ||||
| r_global_vertices, | r_global_vertices, | ||||
| Context not available. | |||||
| state_shaded_smooth); | state_shaded_smooth); | ||||
| } | } | ||||
| /* Faces. */ | /* Faces. */ | ||||
| else if (line.startswith("l ")) { | else if (parse_keyword(line, "l")) { | ||||
| line = line.drop_prefix(2); | |||||
| geom_add_edge(curr_geom, line, offsets, r_global_vertices); | geom_add_edge(curr_geom, line, offsets, r_global_vertices); | ||||
| } | } | ||||
| /* Objects. */ | /* Objects. */ | ||||
| else if (line.startswith("o ")) { | else if (parse_keyword(line, "o")) { | ||||
| line = line.drop_prefix(2); | |||||
| state_shaded_smooth = false; | state_shaded_smooth = false; | ||||
| state_group_name = ""; | state_group_name = ""; | ||||
| state_material_name = ""; | state_material_name = ""; | ||||
| curr_geom = create_geometry( | curr_geom = create_geometry( | ||||
| curr_geom, GEOM_MESH, line, r_global_vertices, r_all_geometries, offsets); | curr_geom, GEOM_MESH, line.trim(), r_global_vertices, r_all_geometries, offsets); | ||||
| } | } | ||||
| /* Groups. */ | /* Groups. */ | ||||
| else if (line.startswith("g ")) { | else if (parse_keyword(line, "g")) { | ||||
| line = line.drop_prefix(2); | geom_update_group(line.trim(), state_group_name); | ||||
| geom_update_group(line, state_group_name); | |||||
| int new_index = curr_geom->group_indices_.size(); | int new_index = curr_geom->group_indices_.size(); | ||||
| state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index); | state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index); | ||||
| if (new_index == state_group_index) { | if (new_index == state_group_index) { | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| /* Smoothing groups. */ | /* Smoothing groups. */ | ||||
| else if (line.startswith("s ")) { | else if (parse_keyword(line, "s")) { | ||||
| line = line.drop_prefix(2); | |||||
| geom_update_smooth_group(line, state_shaded_smooth); | geom_update_smooth_group(line, state_shaded_smooth); | ||||
| } | } | ||||
| /* Materials and their libraries. */ | /* Materials and their libraries. */ | ||||
| else if (line.startswith("usemtl ")) { | else if (parse_keyword(line, "usemtl")) { | ||||
| line = line.drop_prefix(7); | state_material_name = line.trim(); | ||||
| state_material_name = line; | |||||
| int new_mat_index = curr_geom->material_indices_.size(); | int new_mat_index = curr_geom->material_indices_.size(); | ||||
| state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name, | state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name, | ||||
| new_mat_index); | new_mat_index); | ||||
| Context not available. | |||||
| curr_geom->material_order_.append(state_material_name); | curr_geom->material_order_.append(state_material_name); | ||||
| } | } | ||||
| } | } | ||||
| else if (line.startswith("mtllib ")) { | else if (parse_keyword(line, "mtllib")) { | ||||
| line = line.drop_prefix(7); | mtl_libraries_.append(line.trim()); | ||||
| mtl_libraries_.append(string(line)); | |||||
| } | } | ||||
| /* Comments. */ | /* Comments. */ | ||||
| else if (line.startswith("#")) { | else if (line.startswith("#")) { | ||||
| /* Nothing to do. */ | /* Nothing to do. */ | ||||
| } | } | ||||
| /* Curve related things. */ | /* Curve related things. */ | ||||
| else if (line.startswith("cstype ")) { | else if (parse_keyword(line, "cstype")) { | ||||
| line = line.drop_prefix(7); | |||||
| curr_geom = geom_set_curve_type( | curr_geom = geom_set_curve_type( | ||||
| curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries); | curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries); | ||||
| } | } | ||||
| else if (line.startswith("deg ")) { | else if (parse_keyword(line, "deg")) { | ||||
| line = line.drop_prefix(4); | |||||
| geom_set_curve_degree(curr_geom, line); | geom_set_curve_degree(curr_geom, line); | ||||
| } | } | ||||
| else if (line.startswith("curv ")) { | else if (parse_keyword(line, "curv")) { | ||||
| line = line.drop_prefix(5); | |||||
| geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices); | geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices); | ||||
| } | } | ||||
| else if (line.startswith("parm ")) { | else if (parse_keyword(line, "parm")) { | ||||
| line = line.drop_prefix(5); | |||||
| geom_add_curve_parameters(curr_geom, line); | geom_add_curve_parameters(curr_geom, line); | ||||
| } | } | ||||
| else if (line.startswith("end")) { | else if (line.startswith("end")) { | ||||
| /* End of curve definition, nothing else to do. */ | /* End of curve definition, nothing else to do. */ | ||||
| } | } | ||||
| else if (line.front() <= ' ') { | |||||
| /* Just whitespace, skip. */ | |||||
| } | |||||
| else { | else { | ||||
| std::cout << "OBJ element not recognised: '" << line << "'" << std::endl; | std::cout << "OBJ element not recognized: '" << line << "'" << std::endl; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line) | static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line) | ||||
| { | { | ||||
| if (line.startswith("map_Kd")) { | if (parse_keyword(line, "map_Kd")) { | ||||
| line = line.drop_prefix(6); | |||||
| return eMTLSyntaxElement::map_Kd; | return eMTLSyntaxElement::map_Kd; | ||||
| } | } | ||||
| if (line.startswith("map_Ks")) { | if (parse_keyword(line, "map_Ks")) { | ||||
| line = line.drop_prefix(6); | |||||
| return eMTLSyntaxElement::map_Ks; | return eMTLSyntaxElement::map_Ks; | ||||
| } | } | ||||
| if (line.startswith("map_Ns")) { | if (parse_keyword(line, "map_Ns")) { | ||||
| line = line.drop_prefix(6); | |||||
| return eMTLSyntaxElement::map_Ns; | return eMTLSyntaxElement::map_Ns; | ||||
| } | } | ||||
| if (line.startswith("map_d")) { | if (parse_keyword(line, "map_d")) { | ||||
| line = line.drop_prefix(5); | |||||
| return eMTLSyntaxElement::map_d; | return eMTLSyntaxElement::map_d; | ||||
| } | } | ||||
| if (line.startswith("refl")) { | if (parse_keyword(line, "refl")) { | ||||
| line = line.drop_prefix(4); | |||||
| return eMTLSyntaxElement::map_refl; | return eMTLSyntaxElement::map_refl; | ||||
| } | } | ||||
| if (line.startswith("map_refl")) { | if (parse_keyword(line, "map_refl")) { | ||||
| line = line.drop_prefix(8); | |||||
| return eMTLSyntaxElement::map_refl; | return eMTLSyntaxElement::map_refl; | ||||
| } | } | ||||
| if (line.startswith("map_Ke")) { | if (parse_keyword(line, "map_Ke")) { | ||||
| line = line.drop_prefix(6); | |||||
| return eMTLSyntaxElement::map_Ke; | return eMTLSyntaxElement::map_Ke; | ||||
| } | } | ||||
| if (line.startswith("bump")) { | if (parse_keyword(line, "bump")) { | ||||
| line = line.drop_prefix(4); | |||||
| return eMTLSyntaxElement::map_Bump; | return eMTLSyntaxElement::map_Bump; | ||||
| } | } | ||||
| if (line.startswith("map_Bump") || line.startswith("map_bump")) { | if (parse_keyword(line, "map_Bump") || parse_keyword(line, "map_bump")) { | ||||
| line = line.drop_prefix(8); | |||||
| return eMTLSyntaxElement::map_Bump; | return eMTLSyntaxElement::map_Bump; | ||||
| } | } | ||||
| return eMTLSyntaxElement::string; | return eMTLSyntaxElement::string; | ||||
| } | } | ||||
| static const std::pair<const char *, int> unsupported_texture_options[] = { | static const std::pair<StringRef, int> unsupported_texture_options[] = { | ||||
| {"-blendu ", 1}, | {"-blendu", 1}, | ||||
| {"-blendv ", 1}, | {"-blendv", 1}, | ||||
| {"-boost ", 1}, | {"-boost", 1}, | ||||
| {"-cc ", 1}, | {"-cc", 1}, | ||||
| {"-clamp ", 1}, | {"-clamp", 1}, | ||||
| {"-imfchan ", 1}, | {"-imfchan", 1}, | ||||
| {"-mm ", 2}, | {"-mm", 2}, | ||||
| {"-t ", 3}, | {"-t", 3}, | ||||
| {"-texres ", 1}, | {"-texres", 1}, | ||||
| }; | }; | ||||
| static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map) | static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map) | ||||
| { | { | ||||
| line = drop_whitespace(line); | line = drop_whitespace(line); | ||||
| if (line.startswith("-o ")) { | if (parse_keyword(line, "-o")) { | ||||
| line = line.drop_prefix(3); | |||||
| line = parse_floats(line, 0.0f, tex_map.translation, 3); | line = parse_floats(line, 0.0f, tex_map.translation, 3); | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (line.startswith("-s ")) { | if (parse_keyword(line, "-s")) { | ||||
| line = line.drop_prefix(3); | |||||
| line = parse_floats(line, 1.0f, tex_map.scale, 3); | line = parse_floats(line, 1.0f, tex_map.scale, 3); | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (line.startswith("-bm ")) { | if (parse_keyword(line, "-bm")) { | ||||
| line = line.drop_prefix(4); | |||||
| line = parse_float(line, 0.0f, material->map_Bump_strength); | line = parse_float(line, 0.0f, material->map_Bump_strength); | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (line.startswith("-type ")) { | if (parse_keyword(line, "-type")) { | ||||
| line = line.drop_prefix(6); | |||||
| line = drop_whitespace(line); | line = drop_whitespace(line); | ||||
| /* Only sphere is supported. */ | /* Only sphere is supported. */ | ||||
| tex_map.projection_type = SHD_PROJ_SPHERE; | tex_map.projection_type = SHD_PROJ_SPHERE; | ||||
| Context not available. | |||||
| } | } | ||||
| /* Check for unsupported options and skip them. */ | /* Check for unsupported options and skip them. */ | ||||
| for (const auto &opt : unsupported_texture_options) { | for (const auto &opt : unsupported_texture_options) { | ||||
| if (line.startswith(opt.first)) { | if (parse_keyword(line, opt.first)) { | ||||
| /* Drop the option name. */ | |||||
| line = line.drop_known_prefix(opt.first); | |||||
| /* Drop the arguments. */ | /* Drop the arguments. */ | ||||
| for (int i = 0; i < opt.second; ++i) { | for (int i = 0; i < opt.second; ++i) { | ||||
| line = drop_whitespace(line); | line = drop_whitespace(line); | ||||
| Context not available. | |||||
| StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len}; | StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len}; | ||||
| while (!buffer_str.is_empty()) { | while (!buffer_str.is_empty()) { | ||||
| StringRef line = read_next_line(buffer_str); | StringRef line = read_next_line(buffer_str); | ||||
| line = drop_whitespace(line); | |||||
| if (line.is_empty()) { | if (line.is_empty()) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (line.startswith("newmtl ")) { | if (parse_keyword(line, "newmtl")) { | ||||
| line = line.drop_prefix(7); | line = line.trim(); | ||||
| if (r_materials.remove_as(line)) { | if (r_materials.remove_as(line)) { | ||||
| std::cerr << "Duplicate material found:'" << line | std::cerr << "Duplicate material found:'" << line | ||||
| << "', using the last encountered Material definition." << std::endl; | << "', using the last encountered Material definition." << std::endl; | ||||
| Context not available. | |||||
| material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get(); | material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get(); | ||||
| } | } | ||||
| else if (material != nullptr) { | else if (material != nullptr) { | ||||
| if (line.startswith("Ns ")) { | if (parse_keyword(line, "Ns")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_float(line, 324.0f, material->Ns); | parse_float(line, 324.0f, material->Ns); | ||||
| } | } | ||||
| else if (line.startswith("Ka ")) { | else if (parse_keyword(line, "Ka")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_floats(line, 0.0f, material->Ka, 3); | parse_floats(line, 0.0f, material->Ka, 3); | ||||
| } | } | ||||
| else if (line.startswith("Kd ")) { | else if (parse_keyword(line, "Kd")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_floats(line, 0.8f, material->Kd, 3); | parse_floats(line, 0.8f, material->Kd, 3); | ||||
| } | } | ||||
| else if (line.startswith("Ks ")) { | else if (parse_keyword(line, "Ks")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_floats(line, 0.5f, material->Ks, 3); | parse_floats(line, 0.5f, material->Ks, 3); | ||||
| } | } | ||||
| else if (line.startswith("Ke ")) { | else if (parse_keyword(line, "Ke")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_floats(line, 0.0f, material->Ke, 3); | parse_floats(line, 0.0f, material->Ke, 3); | ||||
| } | } | ||||
| else if (line.startswith("Ni ")) { | else if (parse_keyword(line, "Ni")) { | ||||
| line = line.drop_prefix(3); | |||||
| parse_float(line, 1.45f, material->Ni); | parse_float(line, 1.45f, material->Ni); | ||||
| } | } | ||||
| else if (line.startswith("d ")) { | else if (parse_keyword(line, "d")) { | ||||
| line = line.drop_prefix(2); | |||||
| parse_float(line, 1.0f, material->d); | parse_float(line, 1.0f, material->d); | ||||
| } | } | ||||
| else if (line.startswith("illum ")) { | else if (parse_keyword(line, "illum")) { | ||||
| line = line.drop_prefix(6); | |||||
| parse_int(line, 2, material->illum); | parse_int(line, 2, material->illum); | ||||
| } | } | ||||
| else { | else { | ||||
| Context not available. | |||||
I guess this is just allowing any control character as "whitespace". This is OK but a little broader than what is typically thought of as whitespace so maybe a comment is in order. Did you not use isspace() because you wanted this to be very fast?
Also, what if line only contains the keyword an no more. Won't this index off the end of line?
What if there is more than one whitespace character following the keyword? Maybe all the code after skips whitespece before looking for whatever comes next, in which case this is fine. But otherwise maybe there should be a loop finding out where the whitespace ends?