Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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.cc792
1 files changed, 433 insertions, 359 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 d3d4e1ba860..3e722f8a0dd 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
@@ -8,7 +8,7 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
-#include "parser_string_utils.hh"
+#include "IO_string_utils.hh"
#include "obj_import_file_reader.hh"
@@ -34,7 +34,8 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
Geometry *g = r_all_geometries.last().get();
g->geom_type_ = new_type;
g->geometry_name_ = name.is_empty() ? "New object" : name;
- r_offset.set_index_offset(global_vertices.vertices.size());
+ g->vertex_start_ = global_vertices.vertices.size();
+ r_offset.set_index_offset(g->vertex_start_);
return g;
};
@@ -66,48 +67,40 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
}
static void geom_add_vertex(Geometry *geom,
- const StringRef rest_line,
+ const StringRef line,
GlobalVertices &r_global_vertices)
{
- float3 curr_vert;
- Vector<StringRef> str_vert_split;
- split_by_char(rest_line, ' ', str_vert_split);
- copy_string_to_float(str_vert_split, FLT_MAX, {curr_vert, 3});
- r_global_vertices.vertices.append(curr_vert);
- geom->vertex_indices_.append(r_global_vertices.vertices.size() - 1);
+ float3 vert;
+ parse_floats(line, FLT_MAX, vert, 3);
+ r_global_vertices.vertices.append(vert);
+ geom->vertex_count_++;
}
static void geom_add_vertex_normal(Geometry *geom,
- const StringRef rest_line,
+ const StringRef line,
GlobalVertices &r_global_vertices)
{
- float3 curr_vert_normal;
- Vector<StringRef> str_vert_normal_split;
- split_by_char(rest_line, ' ', str_vert_normal_split);
- copy_string_to_float(str_vert_normal_split, FLT_MAX, {curr_vert_normal, 3});
- r_global_vertices.vertex_normals.append(curr_vert_normal);
+ float3 normal;
+ parse_floats(line, FLT_MAX, normal, 3);
+ r_global_vertices.vertex_normals.append(normal);
geom->has_vertex_normals_ = true;
}
-static void geom_add_uv_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices)
+static void geom_add_uv_vertex(const StringRef line, GlobalVertices &r_global_vertices)
{
- float2 curr_uv_vert;
- Vector<StringRef> str_uv_vert_split;
- split_by_char(rest_line, ' ', str_uv_vert_split);
- copy_string_to_float(str_uv_vert_split, FLT_MAX, {curr_uv_vert, 2});
- r_global_vertices.uv_vertices.append(curr_uv_vert);
+ float2 uv;
+ parse_floats(line, FLT_MAX, uv, 2);
+ r_global_vertices.uv_vertices.append(uv);
}
static void geom_add_edge(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices)
{
- int edge_v1 = -1, edge_v2 = -1;
- Vector<StringRef> str_edge_split;
- split_by_char(rest_line, ' ', str_edge_split);
- copy_string_to_int(str_edge_split[0], -1, edge_v1);
- copy_string_to_int(str_edge_split[1], -1, edge_v2);
+ int edge_v1, edge_v2;
+ line = parse_int(line, -1, edge_v1);
+ line = parse_int(line, -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;
@@ -116,78 +109,45 @@ static void geom_add_edge(Geometry *geom,
}
static void geom_add_polygon(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const GlobalVertices &global_vertices,
const VertexIndexOffset &offsets,
- const StringRef state_material_name,
- const StringRef state_object_group,
- const bool state_shaded_smooth)
+ const int material_index,
+ const int group_index,
+ const bool shaded_smooth)
{
PolyElem curr_face;
- curr_face.shaded_smooth = state_shaded_smooth;
- if (!state_material_name.is_empty()) {
- curr_face.material_name = state_material_name;
- }
- if (!state_object_group.is_empty()) {
- curr_face.vertex_group = state_object_group;
- /* Yes it repeats several times, but another if-check will not reduce steps either. */
+ curr_face.shaded_smooth = shaded_smooth;
+ curr_face.material_index = material_index;
+ if (group_index >= 0) {
+ curr_face.vertex_group_index = group_index;
geom->use_vertex_groups_ = true;
}
+ const int orig_corners_size = geom->face_corners_.size();
+ curr_face.start_index_ = orig_corners_size;
+
bool face_valid = true;
- Vector<StringRef> str_corners_split;
- split_by_char(rest_line, ' ', str_corners_split);
- for (StringRef str_corner : str_corners_split) {
+ while (!line.is_empty() && face_valid) {
PolyCorner corner;
- const size_t n_slash = std::count(str_corner.begin(), str_corner.end(), '/');
bool got_uv = false, got_normal = false;
- if (n_slash == 0) {
- /* Case: "f v1 v2 v3". */
- copy_string_to_int(str_corner, INT32_MAX, corner.vert_index);
- }
- else if (n_slash == 1) {
- /* Case: "f v1/vt1 v2/vt2 v3/vt3". */
- Vector<StringRef> vert_uv_split;
- split_by_char(str_corner, '/', vert_uv_split);
- if (vert_uv_split.size() != 1 && vert_uv_split.size() != 2) {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
+ /* Parse vertex index. */
+ line = parse_int(line, INT32_MAX, corner.vert_index, false);
+ face_valid &= corner.vert_index != INT32_MAX;
+ if (!line.is_empty() && line[0] == '/') {
+ /* Parse UV index. */
+ line = line.drop_prefix(1);
+ if (!line.is_empty() && line[0] != '/') {
+ line = parse_int(line, INT32_MAX, corner.uv_vert_index, false);
+ got_uv = corner.uv_vert_index != INT32_MAX;
}
- else {
- copy_string_to_int(vert_uv_split[0], INT32_MAX, corner.vert_index);
- if (vert_uv_split.size() == 2) {
- copy_string_to_int(vert_uv_split[1], INT32_MAX, corner.uv_vert_index);
- got_uv = corner.uv_vert_index != INT32_MAX;
- }
+ /* Parse normal index. */
+ if (!line.is_empty() && line[0] == '/') {
+ line = line.drop_prefix(1);
+ line = parse_int(line, INT32_MAX, corner.vertex_normal_index, false);
+ got_normal = corner.uv_vert_index != INT32_MAX;
}
}
- else if (n_slash == 2) {
- /* Case: "f v1//vn1 v2//vn2 v3//vn3". */
- /* Case: "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3". */
- Vector<StringRef> vert_uv_normal_split;
- split_by_char(str_corner, '/', vert_uv_normal_split);
- if (vert_uv_normal_split.size() != 2 && vert_uv_normal_split.size() != 3) {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
- }
- else {
- copy_string_to_int(vert_uv_normal_split[0], INT32_MAX, corner.vert_index);
- if (vert_uv_normal_split.size() == 3) {
- copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.uv_vert_index);
- got_uv = corner.uv_vert_index != INT32_MAX;
- copy_string_to_int(vert_uv_normal_split[2], INT32_MAX, corner.vertex_normal_index);
- got_normal = corner.vertex_normal_index != INT32_MAX;
- }
- else {
- copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.vertex_normal_index);
- got_normal = corner.vertex_normal_index != INT32_MAX;
- }
- }
- }
- else {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
- }
/* 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;
@@ -221,19 +181,28 @@ static void geom_add_polygon(Geometry *geom,
face_valid = false;
}
}
- curr_face.face_corners.append(corner);
+ geom->face_corners_.append(corner);
+ curr_face.corner_count_++;
+
+ /* Skip whitespace to get to the next face corner. */
+ line = drop_whitespace(line);
}
if (face_valid) {
geom->face_elements_.append(curr_face);
- geom->total_loops_ += curr_face.face_corners.size();
+ geom->total_loops_ += curr_face.corner_count_;
+ }
+ else {
+ /* Remove just-added corners for the invalid face. */
+ geom->face_corners_.resize(orig_corners_size);
+ geom->has_invalid_polys_ = true;
}
}
static Geometry *geom_set_curve_type(Geometry *geom,
const StringRef rest_line,
const GlobalVertices &global_vertices,
- const StringRef state_object_group,
+ const StringRef group_name,
VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
@@ -242,254 +211,411 @@ static Geometry *geom_set_curve_type(Geometry *geom,
return geom;
}
geom = create_geometry(
- geom, GEOM_CURVE, state_object_group, global_vertices, r_all_geometries, r_offsets);
- geom->nurbs_element_.group_ = state_object_group;
+ geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets);
+ geom->nurbs_element_.group_ = group_name;
return geom;
}
-static void geom_set_curve_degree(Geometry *geom, const StringRef rest_line)
+static void geom_set_curve_degree(Geometry *geom, const StringRef line)
{
- copy_string_to_int(rest_line, 3, geom->nurbs_element_.degree);
+ parse_int(line, 3, geom->nurbs_element_.degree);
}
static void geom_add_curve_vertex_indices(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const GlobalVertices &global_vertices)
{
- Vector<StringRef> str_curv_split;
- split_by_char(rest_line, ' ', str_curv_split);
- /* Remove "0.0" and "1.0" from the strings. They are hardcoded. */
- str_curv_split.remove(0);
- str_curv_split.remove(0);
- geom->nurbs_element_.curv_indices.resize(str_curv_split.size());
- copy_string_to_int(str_curv_split, INT32_MAX, geom->nurbs_element_.curv_indices);
- for (int &curv_index : geom->nurbs_element_.curv_indices) {
+ /* Curve lines always have "0.0" and "1.0", skip over them. */
+ float dummy[2];
+ line = parse_floats(line, 0, dummy, 2);
+ /* Parse indices. */
+ while (!line.is_empty()) {
+ int index;
+ line = parse_int(line, INT32_MAX, index);
+ if (index == INT32_MAX) {
+ return;
+ }
/* Always keep stored indices non-negative and zero-based. */
- curv_index += curv_index < 0 ? global_vertices.vertices.size() : -1;
+ index += index < 0 ? global_vertices.vertices.size() : -1;
+ geom->nurbs_element_.curv_indices.append(index);
}
}
-static void geom_add_curve_parameters(Geometry *geom, const StringRef rest_line)
+static void geom_add_curve_parameters(Geometry *geom, StringRef line)
{
- Vector<StringRef> str_parm_split;
- split_by_char(rest_line, ' ', str_parm_split);
- if (str_parm_split[0] != "u" && str_parm_split[0] != "v") {
- std::cerr << "Surfaces are not supported:'" << str_parm_split[0] << "'" << std::endl;
+ line = drop_whitespace(line);
+ if (line.is_empty()) {
+ std::cerr << "Invalid OBJ curve parm line: '" << line << "'" << std::endl;
+ return;
+ }
+ if (line[0] != 'u') {
+ std::cerr << "OBJ curve surfaces are not supported: '" << line[0] << "'" << std::endl;
return;
}
- str_parm_split.remove(0);
- geom->nurbs_element_.parm.resize(str_parm_split.size());
- copy_string_to_float(str_parm_split, FLT_MAX, geom->nurbs_element_.parm);
+ line = line.drop_prefix(1);
+
+ while (!line.is_empty()) {
+ float val;
+ line = parse_float(line, FLT_MAX, val);
+ if (val != FLT_MAX) {
+ geom->nurbs_element_.parm.append(val);
+ }
+ else {
+ std::cerr << "OBJ curve parm line has invalid number" << std::endl;
+ return;
+ }
+ }
}
-static void geom_update_object_group(const StringRef rest_line, std::string &r_state_object_group)
+static void geom_update_group(const StringRef rest_line, std::string &r_group_name)
{
if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos ||
rest_line.find("default") != string::npos) {
/* Set group for future elements like faces or curves to empty. */
- r_state_object_group = "";
+ r_group_name = "";
return;
}
- r_state_object_group = rest_line;
-}
-
-static void geom_update_polygon_material(Geometry *geom,
- const StringRef rest_line,
- std::string &r_state_material_name)
-{
- /* Materials may repeat if faces are written without sorting. */
- geom->material_names_.add(string(rest_line));
- r_state_material_name = rest_line;
+ r_group_name = rest_line;
}
-static void geom_update_smooth_group(const StringRef rest_line, bool &r_state_shaded_smooth)
+static void geom_update_smooth_group(StringRef line, bool &r_state_shaded_smooth)
{
+ line = drop_whitespace(line);
/* Some implementations use "0" and "null" too, in addition to "off". */
- if (rest_line != "0" && rest_line.find("off") == StringRef::not_found &&
- rest_line.find("null") == StringRef::not_found) {
- int smooth = 0;
- copy_string_to_int(rest_line, 0, smooth);
- r_state_shaded_smooth = smooth != 0;
- }
- else {
- /* The OBJ file explicitly set shading to off. */
+ if (line == "0" || line.startswith("off") || line.startswith("null")) {
r_state_shaded_smooth = false;
+ return;
}
+
+ int smooth = 0;
+ parse_int(line, 0, smooth);
+ r_state_shaded_smooth = smooth != 0;
}
-OBJParser::OBJParser(const OBJImportParams &import_params) : import_params_(import_params)
+OBJParser::OBJParser(const OBJImportParams &import_params, size_t read_buffer_size = 64 * 1024)
+ : import_params_(import_params), read_buffer_size_(read_buffer_size)
{
- obj_file_.open(import_params_.filepath);
- if (!obj_file_.good()) {
+ obj_file_ = BLI_fopen(import_params_.filepath, "rb");
+ if (!obj_file_) {
fprintf(stderr, "Cannot read from OBJ file:'%s'.\n", import_params_.filepath);
return;
}
}
+OBJParser::~OBJParser()
+{
+ if (obj_file_) {
+ fclose(obj_file_);
+ }
+}
+
void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices)
{
- if (!obj_file_.good()) {
+ if (!obj_file_) {
return;
}
- string line;
- /* Store vertex coordinates that belong to other Geometry instances. */
VertexIndexOffset offsets;
- /* Non owning raw pointer to a Geometry. To be updated while creating a new Geometry. */
Geometry *curr_geom = create_geometry(
nullptr, GEOM_MESH, "", r_global_vertices, r_all_geometries, offsets);
- /* State-setting variables: if set, they remain the same for the remaining
+ /* State variables: once set, they remain the same for the remaining
* elements in the object. */
bool state_shaded_smooth = false;
- string state_object_group;
+ string state_group_name;
+ int state_group_index = -1;
string state_material_name;
+ int state_material_index = -1;
+
+ /* Read the input file in chunks. We need up to twice the possible chunk size,
+ * to possibly store remainder of the previous input line that got broken mid-chunk. */
+ Array<char> buffer(read_buffer_size_ * 2);
+
+ size_t buffer_offset = 0;
+ size_t line_number = 0;
+ while (true) {
+ /* Read a chunk of input from the file. */
+ size_t bytes_read = fread(buffer.data() + buffer_offset, 1, read_buffer_size_, obj_file_);
+ if (bytes_read == 0 && buffer_offset == 0) {
+ break; /* No more data to read. */
+ }
- while (std::getline(obj_file_, line)) {
- /* Keep reading new lines if the last character is `\`. */
- /* Another way is to make a getline wrapper and use it in the while condition. */
- read_next_line(obj_file_, line);
+ /* Ensure buffer ends in a newline. */
+ if (bytes_read < read_buffer_size_) {
+ if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') {
+ buffer[buffer_offset + bytes_read] = '\n';
+ bytes_read++;
+ }
+ }
- StringRef line_key, rest_line;
- split_line_key_rest(line, line_key, rest_line);
- if (line.empty() || rest_line.is_empty()) {
- continue;
+ size_t buffer_end = buffer_offset + bytes_read;
+ if (buffer_end == 0) {
+ break;
}
- switch (line_key_str_to_enum(line_key)) {
- case eOBJLineKey::V: {
- geom_add_vertex(curr_geom, rest_line, r_global_vertices);
- break;
+
+ /* Find last newline. */
+ size_t last_nl = buffer_end;
+ while (last_nl > 0) {
+ --last_nl;
+ if (buffer[last_nl] == '\n') {
+ if (last_nl < 1 || buffer[last_nl - 1] != '\\') {
+ break;
+ }
}
- case eOBJLineKey::VN: {
- geom_add_vertex_normal(curr_geom, rest_line, r_global_vertices);
- break;
+ }
+ if (buffer[last_nl] != '\n') {
+ /* Whole line did not fit into our read buffer. Warn and exit. */
+ fprintf(stderr,
+ "OBJ file contains a line #%zu that is too long (max. length %zu)\n",
+ line_number,
+ read_buffer_size_);
+ break;
+ }
+ ++last_nl;
+
+ /* Parse the buffer (until last newline) that we have so far,
+ * line by line. */
+ StringRef buffer_str{buffer.data(), (int64_t)last_nl};
+ while (!buffer_str.is_empty()) {
+ StringRef line = read_next_line(buffer_str);
+ ++line_number;
+ if (line.is_empty()) {
+ continue;
}
- case eOBJLineKey::VT: {
- geom_add_uv_vertex(rest_line, r_global_vertices);
- break;
+ /* Most common things that start with 'v': vertices, normals, UVs. */
+ if (line[0] == 'v') {
+ if (line.startswith("v ")) {
+ line = line.drop_prefix(2);
+ geom_add_vertex(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("vn ")) {
+ line = line.drop_prefix(3);
+ geom_add_vertex_normal(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("vt ")) {
+ line = line.drop_prefix(3);
+ geom_add_uv_vertex(line, r_global_vertices);
+ }
}
- case eOBJLineKey::F: {
+ /* Faces. */
+ else if (line.startswith("f ")) {
+ line = line.drop_prefix(2);
geom_add_polygon(curr_geom,
- rest_line,
+ line,
r_global_vertices,
offsets,
- state_material_name,
- state_material_name,
+ state_material_index,
+ state_group_index, /* TODO was wrongly material name! */
state_shaded_smooth);
- break;
- }
- case eOBJLineKey::L: {
- geom_add_edge(curr_geom, rest_line, offsets, r_global_vertices);
- break;
- }
- case eOBJLineKey::CSTYPE: {
- curr_geom = geom_set_curve_type(curr_geom,
- rest_line,
- r_global_vertices,
- state_object_group,
- offsets,
- r_all_geometries);
- break;
- }
- case eOBJLineKey::DEG: {
- geom_set_curve_degree(curr_geom, rest_line);
- break;
- }
- case eOBJLineKey::CURV: {
- geom_add_curve_vertex_indices(curr_geom, rest_line, r_global_vertices);
- break;
- }
- case eOBJLineKey::PARM: {
- geom_add_curve_parameters(curr_geom, rest_line);
- break;
- }
- case eOBJLineKey::O: {
+ }
+ /* Faces. */
+ else if (line.startswith("l ")) {
+ line = line.drop_prefix(2);
+ geom_add_edge(curr_geom, line, offsets, r_global_vertices);
+ }
+ /* Objects. */
+ else if (line.startswith("o ")) {
+ line = line.drop_prefix(2);
state_shaded_smooth = false;
- state_object_group = "";
+ state_group_name = "";
state_material_name = "";
curr_geom = create_geometry(
- curr_geom, GEOM_MESH, rest_line, r_global_vertices, r_all_geometries, offsets);
- break;
- }
- case eOBJLineKey::G: {
- geom_update_object_group(rest_line, state_object_group);
- break;
- }
- case eOBJLineKey::S: {
- geom_update_smooth_group(rest_line, state_shaded_smooth);
- break;
- }
- case eOBJLineKey::USEMTL: {
- geom_update_polygon_material(curr_geom, rest_line, state_material_name);
- break;
- }
- case eOBJLineKey::MTLLIB: {
- mtl_libraries_.append(string(rest_line));
- break;
- }
- case eOBJLineKey::COMMENT:
- break;
- default:
- std::cout << "Element not recognised: '" << line_key << "'" << std::endl;
- break;
+ curr_geom, GEOM_MESH, line, r_global_vertices, r_all_geometries, offsets);
+ }
+ /* Groups. */
+ else if (line.startswith("g ")) {
+ line = line.drop_prefix(2);
+ geom_update_group(line, state_group_name);
+ int new_index = curr_geom->group_indices_.size();
+ state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index);
+ if (new_index == state_group_index) {
+ curr_geom->group_order_.append(state_group_name);
+ }
+ }
+ /* Smoothing groups. */
+ else if (line.startswith("s ")) {
+ line = line.drop_prefix(2);
+ geom_update_smooth_group(line, state_shaded_smooth);
+ }
+ /* Materials and their libraries. */
+ else if (line.startswith("usemtl ")) {
+ line = line.drop_prefix(7);
+ state_material_name = line;
+ int new_mat_index = curr_geom->material_indices_.size();
+ state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name,
+ new_mat_index);
+ if (new_mat_index == state_material_index) {
+ curr_geom->material_order_.append(state_material_name);
+ }
+ }
+ else if (line.startswith("mtllib ")) {
+ line = line.drop_prefix(7);
+ mtl_libraries_.append(string(line));
+ }
+ /* Comments. */
+ else if (line.startswith("#")) {
+ /* Nothing to do. */
+ }
+ /* Curve related things. */
+ else if (line.startswith("cstype ")) {
+ line = line.drop_prefix(7);
+ curr_geom = geom_set_curve_type(
+ curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries);
+ }
+ else if (line.startswith("deg ")) {
+ line = line.drop_prefix(4);
+ geom_set_curve_degree(curr_geom, line);
+ }
+ else if (line.startswith("curv ")) {
+ line = line.drop_prefix(5);
+ geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("parm ")) {
+ line = line.drop_prefix(5);
+ geom_add_curve_parameters(curr_geom, line);
+ }
+ else if (line.startswith("end")) {
+ /* End of curve definition, nothing else to do. */
+ }
+ else {
+ std::cout << "OBJ element not recognised: '" << line << "'" << std::endl;
+ }
}
+
+ /* We might have a line that was cut in the middle by the previous buffer;
+ * copy it over for next chunk reading. */
+ size_t left_size = buffer_end - last_nl;
+ memmove(buffer.data(), buffer.data() + last_nl, left_size);
+ buffer_offset = left_size;
}
}
-/**
- * Skip all texture map options and get the filepath from a "map_" line.
- */
-static StringRef skip_unsupported_options(StringRef line)
+static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line)
{
- TextureMapOptions map_options;
- StringRef last_option;
- int64_t last_option_pos = 0;
-
- /* Find the last texture map option. */
- for (StringRef option : map_options.all_options()) {
- const int64_t pos{line.find(option)};
- /* Equality (>=) takes care of finding an option in the beginning of the line. Avoid messing
- * with signed-unsigned int comparison. */
- if (pos != StringRef::not_found && pos >= last_option_pos) {
- last_option = option;
- last_option_pos = pos;
- }
+ if (line.startswith("map_Kd")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Kd;
}
-
- if (last_option.is_empty()) {
- /* No option found, line is the filepath */
- return line;
+ if (line.startswith("map_Ks")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ks;
+ }
+ if (line.startswith("map_Ns")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ns;
+ }
+ if (line.startswith("map_d")) {
+ line = line.drop_prefix(5);
+ return eMTLSyntaxElement::map_d;
}
+ if (line.startswith("refl")) {
+ line = line.drop_prefix(4);
+ return eMTLSyntaxElement::map_refl;
+ }
+ if (line.startswith("map_refl")) {
+ line = line.drop_prefix(8);
+ return eMTLSyntaxElement::map_refl;
+ }
+ if (line.startswith("map_Ke")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ke;
+ }
+ if (line.startswith("bump")) {
+ line = line.drop_prefix(4);
+ return eMTLSyntaxElement::map_Bump;
+ }
+ if (line.startswith("map_Bump") || line.startswith("map_bump")) {
+ line = line.drop_prefix(8);
+ return eMTLSyntaxElement::map_Bump;
+ }
+ return eMTLSyntaxElement::string;
+}
- /* Remove up to start of the last option + size of the last option + space after it. */
- line = line.drop_prefix(last_option_pos + last_option.size() + 1);
- for (int i = 0; i < map_options.number_of_args(last_option); i++) {
- const int64_t pos_space{line.find_first_of(' ')};
- if (pos_space != StringRef::not_found) {
- BLI_assert(pos_space + 1 < line.size());
- line = line.drop_prefix(pos_space + 1);
+static const std::pair<const char *, int> unsupported_texture_options[] = {
+ {"-blendu ", 1},
+ {"-blendv ", 1},
+ {"-boost ", 1},
+ {"-cc ", 1},
+ {"-clamp ", 1},
+ {"-imfchan ", 1},
+ {"-mm ", 2},
+ {"-t ", 3},
+ {"-texres ", 1},
+};
+
+static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map)
+{
+ line = drop_whitespace(line);
+ if (line.startswith("-o ")) {
+ line = line.drop_prefix(3);
+ line = parse_floats(line, 0.0f, tex_map.translation, 3);
+ return true;
+ }
+ if (line.startswith("-s ")) {
+ line = line.drop_prefix(3);
+ line = parse_floats(line, 1.0f, tex_map.scale, 3);
+ return true;
+ }
+ if (line.startswith("-bm ")) {
+ line = line.drop_prefix(4);
+ line = parse_float(line, 0.0f, material->map_Bump_strength);
+ return true;
+ }
+ if (line.startswith("-type ")) {
+ line = line.drop_prefix(6);
+ line = drop_whitespace(line);
+ /* Only sphere is supported. */
+ tex_map.projection_type = SHD_PROJ_SPHERE;
+ if (!line.startswith("sphere")) {
+ std::cerr << "OBJ import: only sphere MTL projection type is supported: '" << line << "'"
+ << std::endl;
+ }
+ line = drop_non_whitespace(line);
+ return true;
+ }
+ /* Check for unsupported options and skip them. */
+ for (const auto &opt : unsupported_texture_options) {
+ if (line.startswith(opt.first)) {
+ /* Drop the option name. */
+ line = line.drop_known_prefix(opt.first);
+ /* Drop the arguments. */
+ for (int i = 0; i < opt.second; ++i) {
+ line = drop_whitespace(line);
+ line = drop_non_whitespace(line);
+ }
+ return true;
}
}
- return line;
+ return false;
}
-/**
- * Fix incoming texture map line keys for variations due to other exporters.
- */
-static string fix_bad_map_keys(StringRef map_key)
+static void parse_texture_map(StringRef line, MTLMaterial *material, const char *mtl_dir_path)
{
- string new_map_key(map_key);
- if (map_key == "refl") {
- new_map_key = "map_refl";
+ bool is_map = line.startswith("map_");
+ bool is_refl = line.startswith("refl");
+ bool is_bump = line.startswith("bump");
+ if (!is_map && !is_refl && !is_bump) {
+ return;
+ }
+ eMTLSyntaxElement key = mtl_line_start_to_enum(line);
+ if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) {
+ /* No supported texture map found. */
+ std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl;
+ return;
}
- if (map_key.find("bump") != StringRef::not_found) {
- /* Handles both "bump" and "map_Bump" */
- new_map_key = "map_Bump";
+ tex_map_XX &tex_map = material->texture_maps.lookup(key);
+ tex_map.mtl_dir_path = mtl_dir_path;
+
+ /* Parse texture map options. */
+ while (parse_texture_option(line, material, tex_map)) {
}
- return new_map_key;
+
+ /* What remains is the image path. */
+ line = line.trim();
+ tex_map.image_path = line;
}
Span<std::string> OBJParser::mtl_libraries() const
@@ -503,125 +629,73 @@ MTLParser::MTLParser(StringRef mtl_library, StringRefNull obj_filepath)
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_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR);
- mtl_file_.open(mtl_file_path_);
- if (!mtl_file_.good()) {
- fprintf(stderr, "Cannot read from MTL file:'%s'\n", mtl_file_path_);
- return;
- }
}
-void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mtl_materials)
+void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_materials)
{
- if (!mtl_file_.good()) {
+ size_t buffer_len;
+ void *buffer = BLI_file_read_text_as_mem(mtl_file_path_, 0, &buffer_len);
+ if (buffer == nullptr) {
+ fprintf(stderr, "OBJ import: cannot read from MTL file: '%s'\n", mtl_file_path_);
return;
}
- string line;
- MTLMaterial *current_mtlmaterial = nullptr;
+ MTLMaterial *material = nullptr;
- while (std::getline(mtl_file_, line)) {
- StringRef line_key, rest_line;
- split_line_key_rest(line, line_key, rest_line);
- if (line.empty() || rest_line.is_empty()) {
+ StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len};
+ while (!buffer_str.is_empty()) {
+ StringRef line = read_next_line(buffer_str);
+ if (line.is_empty()) {
continue;
}
- /* Fix lower case/ incomplete texture map identifiers. */
- const string fixed_key = fix_bad_map_keys(line_key);
- line_key = fixed_key;
-
- if (line_key == "newmtl") {
- if (r_mtl_materials.remove_as(rest_line)) {
- std::cerr << "Duplicate material found:'" << rest_line
+ if (line.startswith("newmtl ")) {
+ line = line.drop_prefix(7);
+ if (r_materials.remove_as(line)) {
+ std::cerr << "Duplicate material found:'" << line
<< "', using the last encountered Material definition." << std::endl;
}
- current_mtlmaterial =
- r_mtl_materials.lookup_or_add(string(rest_line), std::make_unique<MTLMaterial>()).get();
- }
- else if (line_key == "Ns") {
- copy_string_to_float(rest_line, 324.0f, current_mtlmaterial->Ns);
- }
- else if (line_key == "Ka") {
- Vector<StringRef> str_ka_split;
- split_by_char(rest_line, ' ', str_ka_split);
- copy_string_to_float(str_ka_split, 0.0f, {current_mtlmaterial->Ka, 3});
- }
- else if (line_key == "Kd") {
- Vector<StringRef> str_kd_split;
- split_by_char(rest_line, ' ', str_kd_split);
- copy_string_to_float(str_kd_split, 0.8f, {current_mtlmaterial->Kd, 3});
- }
- else if (line_key == "Ks") {
- Vector<StringRef> str_ks_split;
- split_by_char(rest_line, ' ', str_ks_split);
- copy_string_to_float(str_ks_split, 0.5f, {current_mtlmaterial->Ks, 3});
- }
- else if (line_key == "Ke") {
- Vector<StringRef> str_ke_split;
- split_by_char(rest_line, ' ', str_ke_split);
- copy_string_to_float(str_ke_split, 0.0f, {current_mtlmaterial->Ke, 3});
- }
- else if (line_key == "Ni") {
- copy_string_to_float(rest_line, 1.45f, current_mtlmaterial->Ni);
- }
- else if (line_key == "d") {
- copy_string_to_float(rest_line, 1.0f, current_mtlmaterial->d);
+ material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get();
}
- else if (line_key == "illum") {
- copy_string_to_int(rest_line, 2, current_mtlmaterial->illum);
- }
-
- /* Parse image textures. */
- else if (line_key.find("map_") != StringRef::not_found) {
- /* TODO(@howardt): fix this. */
- eMTLSyntaxElement line_key_enum = mtl_line_key_str_to_enum(line_key);
- if (line_key_enum == eMTLSyntaxElement::string ||
- !current_mtlmaterial->texture_maps.contains_as(line_key_enum)) {
- /* No supported texture map found. */
- std::cerr << "Texture map type not supported:'" << line_key << "'" << std::endl;
- continue;
+ else if (material != nullptr) {
+ if (line.startswith("Ns ")) {
+ line = line.drop_prefix(3);
+ parse_float(line, 324.0f, material->Ns);
}
- tex_map_XX &tex_map = current_mtlmaterial->texture_maps.lookup(line_key_enum);
- Vector<StringRef> str_map_xx_split;
- split_by_char(rest_line, ' ', str_map_xx_split);
-
- /* TODO(@ankitm): use `skip_unsupported_options` for parsing these options too? */
- const int64_t pos_o{str_map_xx_split.first_index_of_try("-o")};
- if (pos_o != -1 && pos_o + 3 < str_map_xx_split.size()) {
- copy_string_to_float({str_map_xx_split[pos_o + 1],
- str_map_xx_split[pos_o + 2],
- str_map_xx_split[pos_o + 3]},
- 0.0f,
- {tex_map.translation, 3});
- }
- const int64_t pos_s{str_map_xx_split.first_index_of_try("-s")};
- if (pos_s != -1 && pos_s + 3 < str_map_xx_split.size()) {
- copy_string_to_float({str_map_xx_split[pos_s + 1],
- str_map_xx_split[pos_s + 2],
- str_map_xx_split[pos_s + 3]},
- 1.0f,
- {tex_map.scale, 3});
- }
- /* Only specific to Normal Map node. */
- const int64_t pos_bm{str_map_xx_split.first_index_of_try("-bm")};
- if (pos_bm != -1 && pos_bm + 1 < str_map_xx_split.size()) {
- copy_string_to_float(
- str_map_xx_split[pos_bm + 1], 0.0f, current_mtlmaterial->map_Bump_strength);
- }
- const int64_t pos_projection{str_map_xx_split.first_index_of_try("-type")};
- if (pos_projection != -1 && pos_projection + 1 < str_map_xx_split.size()) {
- /* Only Sphere is supported, so whatever the type is, set it to Sphere. */
- tex_map.projection_type = SHD_PROJ_SPHERE;
- if (str_map_xx_split[pos_projection + 1] != "sphere") {
- std::cerr << "Using projection type 'sphere', not:'"
- << str_map_xx_split[pos_projection + 1] << "'." << std::endl;
- }
+ else if (line.startswith("Ka ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.0f, material->Ka, 3);
+ }
+ else if (line.startswith("Kd ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.8f, material->Kd, 3);
+ }
+ else if (line.startswith("Ks ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.5f, material->Ks, 3);
+ }
+ else if (line.startswith("Ke ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.0f, material->Ke, 3);
+ }
+ else if (line.startswith("Ni ")) {
+ line = line.drop_prefix(3);
+ parse_float(line, 1.45f, material->Ni);
+ }
+ else if (line.startswith("d ")) {
+ line = line.drop_prefix(2);
+ parse_float(line, 1.0f, material->d);
+ }
+ else if (line.startswith("illum ")) {
+ line = line.drop_prefix(6);
+ parse_int(line, 2, material->illum);
+ }
+ else {
+ parse_texture_map(line, material, mtl_dir_path_);
}
-
- /* Skip all unsupported options and arguments. */
- tex_map.image_path = string(skip_unsupported_options(rest_line));
- tex_map.mtl_dir_path = mtl_dir_path_;
}
}
+
+ MEM_freeN(buffer);
}
} // namespace blender::io::obj