diff --git a/experimental/optimized-parse.cc b/experimental/optimized-parse.cc index 696c7a8..2d359e9 100644 --- a/experimental/optimized-parse.cc +++ b/experimental/optimized-parse.cc @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include // C++11 #include // C++11 @@ -238,6 +240,38 @@ class StackVector // ---------------------------------------------------------------------------- +typedef struct { + std::string name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + float dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::map unknown_parameter; +} material_t; + +typedef struct { + std::string name; // group name or object name. + unsigned int face_offset; + unsigned int length; +} shape_t; + typedef StackVector ShortString; #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) @@ -605,6 +639,254 @@ static inline void parseFloat3(float *x, float *y, float *z, (*z) = parseFloat(token); } +static void InitMaterial(material_t *material) { + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; + } + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; + material->unknown_parameter.clear(); +} + +static void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream) { + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + size_t maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (inStream->peek() != -1) { + inStream->getline(&buf[0], static_cast(maxchars)); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + // set new mtl name + char namebuf[4096]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + sscanf(token, "%s", namebuf); +#endif + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if (token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseFloat(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + float r, g, b; + parseFloat3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseFloat(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseFloat(&token); + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + // Invert value of Tr(assume Tr is in range [0, 1]) + material.dissolve = 1.0f - parseFloat(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + material.ambient_texname = token; + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + material.diffuse_texname = token; + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_texname = token; + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + material.specular_highlight_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + material.bump_texname = token; + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + material.bump_texname = token; + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + material.displacement_texname = token; + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); +} + typedef enum { COMMAND_EMPTY, @@ -615,6 +897,7 @@ typedef enum COMMAND_G, COMMAND_O, COMMAND_USEMTL, + COMMAND_MTLLIB, } CommandType; @@ -636,6 +919,9 @@ typedef struct const char* material_name; unsigned int material_name_len; + const char* mtllib_name; + unsigned int mtllib_name_len; + CommandType type; } Command; @@ -645,11 +931,13 @@ struct CommandCount size_t num_vn; size_t num_vt; size_t num_f; + size_t num_faces; CommandCount() { num_v = 0; num_vn = 0; num_vt = 0; num_f = 0; + num_faces = 0; } }; @@ -782,8 +1070,8 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang //command->material_name = .insert(command->material_name->end(), namebuf, namebuf + strlen(namebuf)); //command->material_name->push_back('\0'); skip_space(&token); - command->material_name = token; - command->material_name_len = length_until_newline(token, p_len - (token - linebuf)); + command->material_name = p + (token - linebuf); + command->material_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1; command->type = COMMAND_USEMTL; return true; @@ -791,60 +1079,24 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang // load mtl if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - // By specification, `mtllib` should be appaar only once in .obj - char namebuf[8192]; + // By specification, `mtllib` should be appear only once in .obj token += 7; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - //std::string err_mtl; - //std::vector materials; - //bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl); - //if (err) { - // (*err) += err_mtl; - //} - - //if (!ok) { - // return false; - //} - - //if (callback.mtllib_cb) { - // callback.mtllib_cb(user_data, &materials.at(0), - // static_cast(materials.size())); - //} + skip_space(&token); + command->mtllib_name = p + (token - linebuf); + command->mtllib_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1; + command->type = COMMAND_MTLLIB; return true; } // group name if (token[0] == 'g' && IS_SPACE((token[1]))) { -#if 0 - ShortString names[16]; - - int num_names = 0; - - while (!IS_NEW_LINE(token[0])) { - bool ret = parseString(&(names[num_names]), &token); - assert(ret); - token += strspn(token, " \t\r"); // skip tag - num_names++; - } - - assert(num_names > 0); - - int name_idx = 0; - // names[0] must be 'g', so skip the 0th element. - if (num_names > 1) { - name_idx = 1; - } -#endif + // @todo { multiple group name. } token += 2; - command->group_name = token; - command->group_name_len = length_until_newline(token, p_len - (token - linebuf)); + command->group_name = p + (token - linebuf); + command->group_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1; command->type = COMMAND_G; return true; @@ -852,22 +1104,11 @@ static bool parseLine(Command *command, const char *p, size_t p_len, bool triang // object name if (token[0] == 'o' && IS_SPACE((token[1]))) { -#if 0 // @todo { multiple object name? } - char namebuf[8192]; - token += 2; -#ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); -#else - sscanf(token, "%s", namebuf); -#endif - -#endif - token += 2; - command->object_name = token; - command->object_name_len = length_until_newline(token, p_len - (token - linebuf)); + command->object_name = p + (token - linebuf); + command->object_name_len = length_until_newline(token, p_len - (token - linebuf)) + 1; command->type = COMMAND_O; return true; @@ -903,13 +1144,15 @@ static inline bool is_line_ending(const char* p, size_t i, size_t end_i) return false; } -bool parse(std::vector> &vertices, std::vector> &normals, std::vector> &texcoords, std::vector> &faces, const char* buf, size_t len, int req_num_threads) +bool parse(std::vector> &vertices, std::vector> &normals, std::vector> &texcoords, std::vector> &faces, std::vector > &material_ids, std::vector& shapes, const char* buf, size_t len, int req_num_threads) { vertices.clear(); normals.clear(); texcoords.clear(); faces.clear(); + material_ids.clear(); + shapes.clear(); if (len < 1) return false; @@ -930,7 +1173,9 @@ bool parse(std::vector> &vertices, std::vector ms_linedetection; std::chrono::duration ms_alloc; std::chrono::duration ms_parse; + std::chrono::duration ms_load_mtl; std::chrono::duration ms_merge; + std::chrono::duration ms_construct; // 1. Find '\n' and create line data. @@ -1019,6 +1264,9 @@ bool parse(std::vector> &vertices, std::vector> &vertices, std::vectorsize(); + } + commands[t].emplace_back(std::move(command)); + } } @@ -1057,6 +1313,31 @@ bool parse(std::vector> &vertices, std::vector material_map; + std::vector materials; + + // Load material(if exits) + if (mtllib_i_index >= 0 && mtllib_t_index >= 0 && commands[mtllib_t_index][mtllib_i_index].mtllib_name && commands[mtllib_t_index][mtllib_i_index].mtllib_name_len > 0) { + std::string material_filename = std::string(commands[mtllib_t_index][mtllib_i_index].mtllib_name, commands[mtllib_t_index][mtllib_i_index].mtllib_name_len); + std::cout << "mtllib :" << material_filename << std::endl; + + auto t1 = std::chrono::high_resolution_clock::now(); + + std::ifstream ifs(material_filename); + if (ifs.good()) { + LoadMtl(&material_map, &materials, &ifs); + + std::cout << "maetrials = " << materials.size() << std::endl; + + ifs.close(); + } + + auto t2 = std::chrono::high_resolution_clock::now(); + + ms_load_mtl = t2 - t1; } auto command_sum = 0; @@ -1070,11 +1351,13 @@ bool parse(std::vector> &vertices, std::vector> &vertices, std::vector workers; for (size_t t = 0; t < num_threads; t++) { + int material_id = -1; // -1 = default unknown material. workers->push_back(std::thread([&, t]() { size_t v_count = v_offsets[t]; size_t n_count = n_offsets[t]; size_t t_count = t_offsets[t]; size_t f_count = f_offsets[t]; + size_t face_count = face_offsets[t]; for (size_t i = 0; i < commands[t].size(); i++) { if (commands[t][i].type == COMMAND_EMPTY) { continue; + } else if (commands[t][i].type == COMMAND_USEMTL) { + if (commands[t][i].material_name && commands[t][i].material_name_len > 0) { + std::string material_name(commands[t][i].material_name, commands[t][i].material_name_len); + + if (material_map.find(material_name) != material_map.end()) { + material_id = material_map[material_name]; + } else { + // Assign invalid material ID + material_id = -1; + } + } } else if (commands[t][i].type == COMMAND_V) { vertices[3*v_count+0] = commands[t][i].vx; vertices[3*v_count+1] = commands[t][i].vy; @@ -1142,8 +1442,10 @@ bool parse(std::vector> &vertices, std::vector> &vertices, std::vector 0) { + // push previous shape + shape.length = face_count - face_prev_offset; + shapes.push_back(shape); + face_prev_offset = face_count; + } + } + + // redefine shape. + shape.name = name; + shape.face_offset = face_count; + shape.length = 0; + } + } else if (commands[t][i].type == COMMAND_G) { + std::cout << "g" << std::endl; + } + if (commands[t][i].type == COMMAND_F) { + face_count++; + } + } + } + + if ((face_count - face_prev_offset) > 0) { + shape.length = face_count - shape.face_offset; + if (shape.length > 0) { + shapes.push_back(shape); + } + } else { + // Guess no 'v' line occurrence after 'o' or 'g', so discards current shape information. + std::cout << "remainder " << shape.name << ", " << shape.face_offset << ", " << face_prev_offset << ", " << face_count << std::endl; + } + + auto t_end = std::chrono::high_resolution_clock::now(); + + ms_construct = t_end - t_start; + } std::chrono::duration ms_total = t4 - t1; std::cout << "total parsing time: " << ms_total.count() << " ms\n"; @@ -1165,10 +1539,14 @@ bool parse(std::vector> &vertices, std::vector::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); @@ -514,6 +516,8 @@ int main(int argc, char **argv) std::vector> normals; std::vector> texcoords; std::vector> faces; + std::vector> material_ids; + std::vector shapes; size_t data_len = 0; const char* data = get_file_data(&data_len, argv[1]); @@ -522,7 +526,7 @@ int main(int argc, char **argv) return false; } printf("filesize: %d\n", (int)data_len); - bool ret = parse(vertices, normals, texcoords, faces, data, data_len, num_threads); + bool ret = parse(vertices, normals, texcoords, faces, material_ids, shapes, data, data_len, num_threads); return ret; }