| | |
| | |
| | |
| | #define TINYOBJLOADER_IMPLEMENTATION |
| | #include "tiny_obj_loader.h" |
| |
|
| | #include <cassert> |
| | #include <cstdio> |
| | #include <cstdlib> |
| | #include <fstream> |
| | #include <iostream> |
| | #include <sstream> |
| |
|
| | #ifdef _WIN32 |
| | #ifdef __cplusplus |
| | extern "C" { |
| | #endif |
| | #include <windows.h> |
| | #include <mmsystem.h> |
| | #ifdef __cplusplus |
| | } |
| | #endif |
| | #pragma comment(lib, "winmm.lib") |
| | #else |
| | #if defined(__unix__) || defined(__APPLE__) |
| | #include <sys/time.h> |
| | #else |
| | #include <ctime> |
| | #endif |
| | #endif |
| |
|
| | #ifdef __clang__ |
| | #pragma clang diagnostic push |
| | #if __has_warning("-Wzero-as-null-pointer-constant") |
| | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" |
| | #endif |
| | #endif |
| |
|
| | class timerutil { |
| | public: |
| | #ifdef _WIN32 |
| | typedef DWORD time_t; |
| |
|
| | timerutil() { ::timeBeginPeriod(1); } |
| | ~timerutil() { ::timeEndPeriod(1); } |
| |
|
| | void start() { t_[0] = ::timeGetTime(); } |
| | void end() { t_[1] = ::timeGetTime(); } |
| |
|
| | time_t sec() { return (time_t)((t_[1] - t_[0]) / 1000); } |
| | time_t msec() { return (time_t)((t_[1] - t_[0])); } |
| | time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000); } |
| | time_t current() { return ::timeGetTime(); } |
| |
|
| | #else |
| | #if defined(__unix__) || defined(__APPLE__) |
| | typedef unsigned long int time_t; |
| |
|
| | void start() { gettimeofday(tv + 0, &tz); } |
| | void end() { gettimeofday(tv + 1, &tz); } |
| |
|
| | time_t sec() { return static_cast<time_t>(tv[1].tv_sec - tv[0].tv_sec); } |
| | time_t msec() { |
| | return this->sec() * 1000 + |
| | static_cast<time_t>((tv[1].tv_usec - tv[0].tv_usec) / 1000); |
| | } |
| | time_t usec() { |
| | return this->sec() * 1000000 + |
| | static_cast<time_t>(tv[1].tv_usec - tv[0].tv_usec); |
| | } |
| | time_t current() { |
| | struct timeval t; |
| | gettimeofday(&t, NULL); |
| | return static_cast<time_t>(t.tv_sec * 1000 + t.tv_usec); |
| | } |
| |
|
| | #else |
| | |
| | typedef clock_t time_t; |
| |
|
| | void start() { t_[0] = clock(); } |
| | void end() { t_[1] = clock(); } |
| |
|
| | time_t sec() { return (time_t)((t_[1] - t_[0]) / CLOCKS_PER_SEC); } |
| | time_t msec() { return (time_t)((t_[1] - t_[0]) * 1000 / CLOCKS_PER_SEC); } |
| | time_t usec() { return (time_t)((t_[1] - t_[0]) * 1000000 / CLOCKS_PER_SEC); } |
| | time_t current() { return (time_t)clock(); } |
| |
|
| | #endif |
| | #endif |
| |
|
| | private: |
| | #ifdef _WIN32 |
| | DWORD t_[2]; |
| | #else |
| | #if defined(__unix__) || defined(__APPLE__) |
| | struct timeval tv[2]; |
| | struct timezone tz; |
| | #else |
| | time_t t_[2]; |
| | #endif |
| | #endif |
| | }; |
| |
|
| | static void PrintInfo(const tinyobj::attrib_t& attrib, |
| | const std::vector<tinyobj::shape_t>& shapes, |
| | const std::vector<tinyobj::material_t>& materials) { |
| | std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; |
| | std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; |
| | std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) |
| | << std::endl; |
| |
|
| | std::cout << "# of shapes : " << shapes.size() << std::endl; |
| | std::cout << "# of materials : " << materials.size() << std::endl; |
| |
|
| | for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { |
| | printf(" v[%ld] = (%f, %f, %f)\n", static_cast<long>(v), |
| | static_cast<const double>(attrib.vertices[3 * v + 0]), |
| | static_cast<const double>(attrib.vertices[3 * v + 1]), |
| | static_cast<const double>(attrib.vertices[3 * v + 2])); |
| | } |
| |
|
| | for (size_t v = 0; v < attrib.normals.size() / 3; v++) { |
| | printf(" n[%ld] = (%f, %f, %f)\n", static_cast<long>(v), |
| | static_cast<const double>(attrib.normals[3 * v + 0]), |
| | static_cast<const double>(attrib.normals[3 * v + 1]), |
| | static_cast<const double>(attrib.normals[3 * v + 2])); |
| | } |
| |
|
| | for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { |
| | printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v), |
| | static_cast<const double>(attrib.texcoords[2 * v + 0]), |
| | static_cast<const double>(attrib.texcoords[2 * v + 1])); |
| | } |
| |
|
| | |
| | for (size_t i = 0; i < shapes.size(); i++) { |
| | printf("shape[%ld].name = %s\n", static_cast<long>(i), |
| | shapes[i].name.c_str()); |
| | printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast<long>(i), |
| | static_cast<unsigned long>(shapes[i].mesh.indices.size())); |
| | printf("Size of shape[%ld].lines.indices: %lu\n", static_cast<long>(i), |
| | static_cast<unsigned long>(shapes[i].lines.indices.size())); |
| | printf("Size of shape[%ld].points.indices: %lu\n", static_cast<long>(i), |
| | static_cast<unsigned long>(shapes[i].points.indices.size())); |
| |
|
| | size_t index_offset = 0; |
| |
|
| | assert(shapes[i].mesh.num_face_vertices.size() == |
| | shapes[i].mesh.material_ids.size()); |
| |
|
| | assert(shapes[i].mesh.num_face_vertices.size() == |
| | shapes[i].mesh.smoothing_group_ids.size()); |
| |
|
| | printf("shape[%ld].num_faces: %lu\n", static_cast<long>(i), |
| | static_cast<unsigned long>(shapes[i].mesh.num_face_vertices.size())); |
| |
|
| | |
| | for (size_t f = 0; f < shapes[i].mesh.num_face_vertices.size(); f++) { |
| | size_t fnum = shapes[i].mesh.num_face_vertices[f]; |
| |
|
| | printf(" face[%ld].fnum = %ld\n", static_cast<long>(f), |
| | static_cast<unsigned long>(fnum)); |
| |
|
| | |
| | for (size_t v = 0; v < fnum; v++) { |
| | tinyobj::index_t idx = shapes[i].mesh.indices[index_offset + v]; |
| | printf(" face[%ld].v[%ld].idx = %d/%d/%d\n", static_cast<long>(f), |
| | static_cast<long>(v), idx.vertex_index, idx.normal_index, |
| | idx.texcoord_index); |
| | } |
| |
|
| | printf(" face[%ld].material_id = %d\n", static_cast<long>(f), |
| | shapes[i].mesh.material_ids[f]); |
| | printf(" face[%ld].smoothing_group_id = %d\n", static_cast<long>(f), |
| | shapes[i].mesh.smoothing_group_ids[f]); |
| |
|
| | index_offset += fnum; |
| | } |
| |
|
| | printf("shape[%ld].num_tags: %lu\n", static_cast<long>(i), |
| | static_cast<unsigned long>(shapes[i].mesh.tags.size())); |
| | for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) { |
| | printf(" tag[%ld] = %s ", static_cast<long>(t), |
| | shapes[i].mesh.tags[t].name.c_str()); |
| | printf(" ints: ["); |
| | for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) { |
| | printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j])); |
| | if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) { |
| | printf(", "); |
| | } |
| | } |
| | printf("]"); |
| |
|
| | printf(" floats: ["); |
| | for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) { |
| | printf("%f", static_cast<const double>( |
| | shapes[i].mesh.tags[t].floatValues[j])); |
| | if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) { |
| | printf(", "); |
| | } |
| | } |
| | printf("]"); |
| |
|
| | printf(" strings: ["); |
| | for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) { |
| | printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); |
| | if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) { |
| | printf(", "); |
| | } |
| | } |
| | printf("]"); |
| | printf("\n"); |
| | } |
| | } |
| |
|
| | for (size_t i = 0; i < materials.size(); i++) { |
| | printf("material[%ld].name = %s\n", static_cast<long>(i), |
| | materials[i].name.c_str()); |
| | printf(" material.Ka = (%f, %f ,%f)\n", |
| | static_cast<const double>(materials[i].ambient[0]), |
| | static_cast<const double>(materials[i].ambient[1]), |
| | static_cast<const double>(materials[i].ambient[2])); |
| | printf(" material.Kd = (%f, %f ,%f)\n", |
| | static_cast<const double>(materials[i].diffuse[0]), |
| | static_cast<const double>(materials[i].diffuse[1]), |
| | static_cast<const double>(materials[i].diffuse[2])); |
| | printf(" material.Ks = (%f, %f ,%f)\n", |
| | static_cast<const double>(materials[i].specular[0]), |
| | static_cast<const double>(materials[i].specular[1]), |
| | static_cast<const double>(materials[i].specular[2])); |
| | printf(" material.Tr = (%f, %f ,%f)\n", |
| | static_cast<const double>(materials[i].transmittance[0]), |
| | static_cast<const double>(materials[i].transmittance[1]), |
| | static_cast<const double>(materials[i].transmittance[2])); |
| | printf(" material.Ke = (%f, %f ,%f)\n", |
| | static_cast<const double>(materials[i].emission[0]), |
| | static_cast<const double>(materials[i].emission[1]), |
| | static_cast<const double>(materials[i].emission[2])); |
| | printf(" material.Ns = %f\n", |
| | static_cast<const double>(materials[i].shininess)); |
| | printf(" material.Ni = %f\n", static_cast<const double>(materials[i].ior)); |
| | printf(" material.dissolve = %f\n", |
| | static_cast<const double>(materials[i].dissolve)); |
| | printf(" material.illum = %d\n", materials[i].illum); |
| | printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); |
| | printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); |
| | printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); |
| | printf(" material.map_Ns = %s\n", |
| | materials[i].specular_highlight_texname.c_str()); |
| | printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); |
| | printf(" bump_multiplier = %f\n", static_cast<const double>(materials[i].bump_texopt.bump_multiplier)); |
| | printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); |
| | printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); |
| | printf(" <<PBR>>\n"); |
| | printf(" material.Pr = %f\n", static_cast<const double>(materials[i].roughness)); |
| | printf(" material.Pm = %f\n", static_cast<const double>(materials[i].metallic)); |
| | printf(" material.Ps = %f\n", static_cast<const double>(materials[i].sheen)); |
| | printf(" material.Pc = %f\n", static_cast<const double>(materials[i].clearcoat_thickness)); |
| | printf(" material.Pcr = %f\n", static_cast<const double>(materials[i].clearcoat_thickness)); |
| | printf(" material.aniso = %f\n", static_cast<const double>(materials[i].anisotropy)); |
| | printf(" material.anisor = %f\n", static_cast<const double>(materials[i].anisotropy_rotation)); |
| | printf(" material.map_Ke = %s\n", materials[i].emissive_texname.c_str()); |
| | printf(" material.map_Pr = %s\n", materials[i].roughness_texname.c_str()); |
| | printf(" material.map_Pm = %s\n", materials[i].metallic_texname.c_str()); |
| | printf(" material.map_Ps = %s\n", materials[i].sheen_texname.c_str()); |
| | printf(" material.norm = %s\n", materials[i].normal_texname.c_str()); |
| | std::map<std::string, std::string>::const_iterator it( |
| | materials[i].unknown_parameter.begin()); |
| | std::map<std::string, std::string>::const_iterator itEnd( |
| | materials[i].unknown_parameter.end()); |
| |
|
| | for (; it != itEnd; it++) { |
| | printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); |
| | } |
| | printf("\n"); |
| | } |
| | } |
| |
|
| | static bool TestLoadObj(const char* filename, const char* basepath = NULL, |
| | bool triangulate = true) { |
| | std::cout << "Loading " << filename << std::endl; |
| |
|
| | tinyobj::attrib_t attrib; |
| | std::vector<tinyobj::shape_t> shapes; |
| | std::vector<tinyobj::material_t> materials; |
| |
|
| | timerutil t; |
| | t.start(); |
| | std::string warn; |
| | std::string err; |
| | bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, |
| | basepath, triangulate); |
| | t.end(); |
| | printf("Parsing time: %lu [msecs]\n", t.msec()); |
| |
|
| | if (!warn.empty()) { |
| | std::cout << "WARN: " << warn << std::endl; |
| | } |
| |
|
| | if (!err.empty()) { |
| | std::cerr << "ERR: " << err << std::endl; |
| | } |
| |
|
| | if (!ret) { |
| | printf("Failed to load/parse .obj.\n"); |
| | return false; |
| | } |
| |
|
| | PrintInfo(attrib, shapes, materials); |
| |
|
| | return true; |
| | } |
| |
|
| | static bool TestStreamLoadObj() { |
| | std::cout << "Stream Loading " << std::endl; |
| |
|
| | std::stringstream objStream; |
| | objStream << "mtllib cube.mtl\n" |
| | "\n" |
| | "v 0.000000 2.000000 2.000000\n" |
| | "v 0.000000 0.000000 2.000000\n" |
| | "v 2.000000 0.000000 2.000000\n" |
| | "v 2.000000 2.000000 2.000000\n" |
| | "v 0.000000 2.000000 0.000000\n" |
| | "v 0.000000 0.000000 0.000000\n" |
| | "v 2.000000 0.000000 0.000000\n" |
| | "v 2.000000 2.000000 0.000000\n" |
| | "# 8 vertices\n" |
| | "\n" |
| | "g front cube\n" |
| | "usemtl white\n" |
| | "f 1 2 3 4\n" |
| | "g back cube\n" |
| | "# expects white material\n" |
| | "f 8 7 6 5\n" |
| | "g right cube\n" |
| | "usemtl red\n" |
| | "f 4 3 7 8\n" |
| | "g top cube\n" |
| | "usemtl white\n" |
| | "f 5 1 4 8\n" |
| | "g left cube\n" |
| | "usemtl green\n" |
| | "f 5 6 2 1\n" |
| | "g bottom cube\n" |
| | "usemtl white\n" |
| | "f 2 6 7 3\n" |
| | "# 6 elements"; |
| |
|
| | std::string matStream( |
| | "newmtl white\n" |
| | "Ka 0 0 0\n" |
| | "Kd 1 1 1\n" |
| | "Ks 0 0 0\n" |
| | "\n" |
| | "newmtl red\n" |
| | "Ka 0 0 0\n" |
| | "Kd 1 0 0\n" |
| | "Ks 0 0 0\n" |
| | "\n" |
| | "newmtl green\n" |
| | "Ka 0 0 0\n" |
| | "Kd 0 1 0\n" |
| | "Ks 0 0 0\n" |
| | "\n" |
| | "newmtl blue\n" |
| | "Ka 0 0 0\n" |
| | "Kd 0 0 1\n" |
| | "Ks 0 0 0\n" |
| | "\n" |
| | "newmtl light\n" |
| | "Ka 20 20 20\n" |
| | "Kd 1 1 1\n" |
| | "Ks 0 0 0"); |
| |
|
| | using namespace tinyobj; |
| | class MaterialStringStreamReader : public MaterialReader { |
| | public: |
| | MaterialStringStreamReader(const std::string& matSStream) |
| | : m_matSStream(matSStream) {} |
| | virtual ~MaterialStringStreamReader() TINYOBJ_OVERRIDE {} |
| | virtual bool operator()(const std::string& matId, |
| | std::vector<material_t>* materials, |
| | std::map<std::string, int>* matMap, |
| | std::string* warn, |
| | std::string* err) TINYOBJ_OVERRIDE { |
| | (void)err; |
| | (void)matId; |
| | LoadMtl(matMap, materials, &m_matSStream, warn, err); |
| |
|
| | return true; |
| | } |
| |
|
| | private: |
| | std::stringstream m_matSStream; |
| | }; |
| |
|
| | MaterialStringStreamReader matSSReader(matStream); |
| | tinyobj::attrib_t attrib; |
| | std::vector<tinyobj::shape_t> shapes; |
| | std::vector<tinyobj::material_t> materials; |
| | std::string warn; |
| | std::string err; |
| | bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, &objStream, |
| | &matSSReader); |
| |
|
| | if (!err.empty()) { |
| | std::cerr << err << std::endl; |
| | } |
| |
|
| | if (!ret) { |
| | return false; |
| | } |
| |
|
| | PrintInfo(attrib, shapes, materials); |
| |
|
| | return true; |
| | } |
| |
|
| | int main(int argc, char** argv) { |
| | if (argc > 1) { |
| | const char* basepath = "models/"; |
| | if (argc > 2) { |
| | basepath = argv[2]; |
| | } |
| | assert(true == TestLoadObj(argv[1], basepath)); |
| | } else { |
| | |
| | |
| | assert(true == TestStreamLoadObj()); |
| | assert(true == |
| | TestLoadObj("models/catmark_torus_creases0.obj", "models/", false)); |
| | } |
| |
|
| | return 0; |
| | } |
| |
|