From df984b1dd6758e0b8edf415be501f2b95b9fba25 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sun, 22 Mar 2020 14:12:45 +0100 Subject: parse face information --- source/blender/blenlib/BLI_string_ref.h | 40 +++++++ source/blender/editors/object/object_obj_import.cc | 127 ++++++++++++--------- 2 files changed, 115 insertions(+), 52 deletions(-) diff --git a/source/blender/blenlib/BLI_string_ref.h b/source/blender/blenlib/BLI_string_ref.h index e43b8d59035..eb2b5308ecd 100644 --- a/source/blender/blenlib/BLI_string_ref.h +++ b/source/blender/blenlib/BLI_string_ref.h @@ -121,6 +121,12 @@ class StringRefBase { StringRef strip(ArrayRef chars = {' ', '\t', '\n', '\r'}) const; float to_float(bool *r_success = nullptr) const; + int to_int(bool *r_success = nullptr) const; + + bool contains(char c) const; + + uint first_index_of(char c, uint start = 0) const; + int try_first_index_of(char c, uint start = 0) const; }; /** @@ -368,6 +374,40 @@ inline float StringRefBase::to_float(bool *r_success) const return value; } +inline int StringRefBase::to_int(bool *r_success) const +{ + char *str_with_null = (char *)alloca(m_size + 1); + this->copy_to__with_null(str_with_null); + char *end; + int value = std::strtol(str_with_null, &end, 10); + if (r_success) { + *r_success = str_with_null != end; + } + return value; +} + +inline bool StringRefBase::contains(char c) const +{ + return this->try_first_index_of(c) >= 0; +} + +inline uint StringRefBase::first_index_of(char c, uint start) const +{ + int index = this->try_first_index_of(c, start); + BLI_assert(index >= 0); + return (uint)index; +} + +inline int StringRefBase::try_first_index_of(char c, uint start) const +{ + for (uint i = start; i < m_size; i++) { + if (m_data[i] == c) { + return i; + } + } + return -1; +} + } // namespace BLI #endif /* __BLI_STRING_REF_H__ */ diff --git a/source/blender/editors/object/object_obj_import.cc b/source/blender/editors/object/object_obj_import.cc index 2b89f1dbda7..988abf742ce 100644 --- a/source/blender/editors/object/object_obj_import.cc +++ b/source/blender/editors/object/object_obj_import.cc @@ -57,7 +57,7 @@ class TextLinesReader { } /* The returned string does not necessarily contain the final newline. */ - StringRef read_next_line_chunk(uint approximate_size) + BLI_NOINLINE StringRef read_next_line_chunk(uint approximate_size) { SCOPED_TIMER(__func__); std::lock_guard lock(m_mutex); @@ -195,9 +195,9 @@ struct ObjFileSegment_f : public ObjFileSegment { Vector face_offsets; Vector vertex_counts; - Vector position_indices; - Vector uv_indices; - Vector normal_indices; + Vector v_indices; + Vector vt_indices; + Vector vn_indices; ObjFileSegment_f() : ObjFileSegment(ObjFileSegmentType::f) { @@ -222,42 +222,6 @@ template static uint count_while(StringRef str, const FuncT &fun return count; } -static bool is_whitespace(char c) -{ - return ELEM(c, ' ', '\t', '\r'); -} - -static bool is_not_newline(char c) -{ - return c != '\n'; -} - -static bool is_newline(char c) -{ - return c == '\n'; -} - -static std::pair find_next_word_in_line(StringRef str) -{ - uint offset = 0; - for (char c : str) { - if (!is_whitespace(c)) { - break; - } - offset++; - } - - uint length = 0; - for (char c : str.drop_prefix(offset)) { - if (is_whitespace(c) || c == '\n') { - break; - } - length++; - } - - return {offset, length}; -} - class StringRefStream { private: const char *m_current; @@ -369,9 +333,16 @@ class StringRefStream { return value; } + int extract_next_int(bool *r_success = nullptr) + { + StringRef str = this->extract_next_word(); + int value = str.to_int(r_success); + return value; + } + void forward_over_whitespace() { - while (m_current < m_end && is_whitespace(*m_current)) { + while (m_current < m_end && ELEM(*m_current, ' ', '\t', '\r')) { m_current++; } } @@ -472,7 +443,60 @@ BLI_NOINLINE static void parse_uvs(StringRefStream &stream, Vector &r_uv } } -static std::unique_ptr parse_obj_lines(StringRef orig_str) +BLI_NOINLINE static void parse_faces(StringRefStream &stream, ObjFileSegment_f &segment) +{ + while (stream.peek_word() == "f") { + StringRefStream line = stream.extract_line().drop_prefix("f"); + uint count = 0; + + segment.face_offsets.append(segment.v_indices.size()); + + while (true) { + StringRef face_corner = line.extract_next_word(); + if (face_corner.size() == 0) { + break; + } + + int v_index, vt_index, vn_index; + + if (face_corner.contains('/')) { + uint index1 = face_corner.first_index_of('/'); + StringRef first_str = face_corner.substr(0, index1); + v_index = first_str.to_int(); + StringRef remaining_str = face_corner.drop_prefix(index1 + 1); + int index2 = remaining_str.try_first_index_of('/'); + if (index2 == -1) { + vt_index = remaining_str.to_int(); + vn_index = -1; + } + else if (index2 == 0) { + StringRef second_str = remaining_str.drop_prefix('/'); + vt_index = -1; + vn_index = second_str.to_int(); + } + else { + StringRef second_str = remaining_str.substr(0, index2); + StringRef third_str = remaining_str.drop_prefix(index2 + 1); + vt_index = second_str.to_int(); + vn_index = third_str.to_int(); + } + } + else { + v_index = face_corner.to_int(); + vt_index = -1; + vn_index = -1; + } + + segment.v_indices.append(v_index); + segment.vt_indices.append(vt_index); + segment.vn_indices.append(vn_index); + count++; + } + segment.vertex_counts.append(count); + } +} + +BLI_NOINLINE static std::unique_ptr parse_obj_lines(StringRef orig_str) { SCOPED_TIMER(__func__); StringRefStream stream(orig_str); @@ -494,24 +518,18 @@ static std::unique_ptr parse_obj_lines(StringRef orig_str) segments->segments.append(std::move(segment)); } else if (first_word == "v") { - Vector positions; - parse_positions(stream, positions); auto segment = BLI::make_unique(); - segment->positions = std::move(positions); + parse_positions(stream, segment->positions); segments->segments.append(std::move(segment)); } else if (first_word == "vn") { - Vector normals; - parse_normals(stream, normals); auto segment = BLI::make_unique(); - segment->normals = std::move(normals); + parse_normals(stream, segment->normals); segments->segments.append(std::move(segment)); } else if (first_word == "vt") { - Vector uvs; - parse_uvs(stream, uvs); auto segment = BLI::make_unique(); - segment->uvs = std::move(uvs); + parse_uvs(stream, segment->uvs); segments->segments.append(std::move(segment)); } else if (first_word == "usemtl") { @@ -526,6 +544,11 @@ static std::unique_ptr parse_obj_lines(StringRef orig_str) auto segment = BLI::make_unique(smoothing_group_name); segments->segments.append(std::move(segment)); } + else if (first_word == "f") { + auto segment = BLI::make_unique(); + parse_faces(stream, *segment); + segments->segments.append(std::move(segment)); + } else { stream.extract_line(); } @@ -534,7 +557,7 @@ static std::unique_ptr parse_obj_lines(StringRef orig_str) return segments; } -static void import_obj(bContext *UNUSED(C), StringRef file_path) +BLI_NOINLINE static void import_obj(bContext *UNUSED(C), StringRef file_path) { std::ifstream input_stream; input_stream.open(file_path, std::ios::binary); -- cgit v1.2.3