#include "obj-loader.h" #include "nanort.h" // for float3 #define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wreserved-id-macro" #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #pragma clang diagnostic ignored "-Wcast-align" #pragma clang diagnostic ignored "-Wpadded" #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wvariadic-macros" #pragma clang diagnostic ignored "-Wc++11-extensions" #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" #pragma clang diagnostic ignored "-Wimplicit-fallthrough" #if __has_warning("-Wdouble-promotion") #pragma clang diagnostic ignored "-Wdouble-promotion" #endif #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif #if __has_warning("-Wcast-qual") #pragma clang diagnostic ignored "-Wcast-qual" #endif #endif #include "stb_image.h" #ifdef __clang__ #pragma clang diagnostic pop #endif #include #ifdef NANOSG_USE_CXX11 #include #else #include #endif #define USE_TEX_CACHE 1 namespace example { typedef nanort::real3 float3; #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wexit-time-destructors" #pragma clang diagnostic ignored "-Wglobal-constructors" #endif // TODO(LTE): Remove global static definition. #ifdef NANOSG_USE_CXX11 static std::unordered_map hashed_tex; #else static std::map hashed_tex; #endif #ifdef __clang__ #pragma clang diagnostic pop #endif inline void CalcNormal(float3 &N, float3 v0, float3 v1, float3 v2) { float3 v10 = v1 - v0; float3 v20 = v2 - v0; N = vcross(v20, v10); N = vnormalize(N); } static std::string GetBaseDir(const std::string &filepath) { if (filepath.find_last_of("/\\") != std::string::npos) return filepath.substr(0, filepath.find_last_of("/\\")); return ""; } static int LoadTexture(const std::string &filename, std::vector *textures) { int idx; if (filename.empty()) return -1; std::cout << " Loading texture : " << filename << std::endl; Texture texture; // tigra: find in cache. get index if (USE_TEX_CACHE) { if (hashed_tex.find(filename) != hashed_tex.end()) { puts("from cache"); return hashed_tex[filename]; } } int w, h, n; unsigned char *data = stbi_load(filename.c_str(), &w, &h, &n, 0); if (data) { texture.width = w; texture.height = h; texture.components = n; size_t n_elem = size_t(w * h * n); texture.image = new unsigned char[n_elem]; for (size_t i = 0; i < n_elem; i++) { texture.image[i] = data[i]; } free(data); textures->push_back(texture); idx = int(textures->size()) - 1; // tigra: store index to cache if (USE_TEX_CACHE) { hashed_tex[filename] = idx; } return idx; } std::cout << " Failed to load : " << filename << std::endl; return -1; } static void ComputeBoundingBoxOfMesh(float bmin[3], float bmax[3], const example::Mesh &mesh) { bmin[0] = bmin[1] = bmin[2] = std::numeric_limits::max(); bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits::max(); for (size_t i = 0; i < mesh.vertices.size() / 3; i++) { bmin[0] = std::min(bmin[0], mesh.vertices[3 * i + 0]); bmin[1] = std::min(bmin[1], mesh.vertices[3 * i + 1]); bmin[2] = std::min(bmin[1], mesh.vertices[3 * i + 2]); bmax[0] = std::max(bmax[0], mesh.vertices[3 * i + 0]); bmax[1] = std::max(bmax[1], mesh.vertices[3 * i + 1]); bmax[2] = std::max(bmax[2], mesh.vertices[3 * i + 2]); } } bool LoadObj(const std::string &filename, float scale, std::vector > *meshes, std::vector *out_materials, std::vector *out_textures) { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; std::string basedir = GetBaseDir(filename) + "/"; const char *basepath = (basedir.compare("/") == 0) ? NULL : basedir.c_str(); // auto t_start = std::chrono::system_clock::now(); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(), basepath, /* triangulate */ true); // auto t_end = std::chrono::system_clock::now(); // std::chrono::duration ms = t_end - t_start; if (!err.empty()) { std::cerr << err << std::endl; } if (!ret) { return false; } // std::cout << "[LoadOBJ] Parse time : " << ms.count() << " [msecs]" // << std::endl; std::cout << "[LoadOBJ] # of shapes in .obj : " << shapes.size() << std::endl; std::cout << "[LoadOBJ] # of materials in .obj : " << materials.size() << std::endl; { size_t total_num_vertices = 0; size_t total_num_faces = 0; total_num_vertices = attrib.vertices.size() / 3; std::cout << " vertices : " << attrib.vertices.size() / 3 << std::endl; for (size_t i = 0; i < shapes.size(); i++) { std::cout << " shape[" << i << "].name : " << shapes[i].name << std::endl; std::cout << " shape[" << i << "].indices : " << shapes[i].mesh.indices.size() << std::endl; assert((shapes[i].mesh.indices.size() % 3) == 0); total_num_faces += shapes[i].mesh.indices.size() / 3; // tigra: empty name convert to _id if (shapes[i].name.length() == 0) { #ifdef NANOSG_USE_CXX11 shapes[i].name = "_" + std::to_string(i); #else std::stringstream ss; ss << i; shapes[i].name = "_" + ss.str(); #endif std::cout << " EMPTY shape[" << i << "].name, new : " << shapes[i].name << std::endl; } } std::cout << "[LoadOBJ] # of faces: " << total_num_faces << std::endl; std::cout << "[LoadOBJ] # of vertices: " << total_num_vertices << std::endl; } // TODO(LTE): Implement tangents and binormals for (size_t i = 0; i < shapes.size(); i++) { Mesh mesh(/* stride */ sizeof(float) * 3); mesh.name = shapes[i].name; const size_t num_faces = shapes[i].mesh.indices.size() / 3; mesh.faces.resize(num_faces * 3); mesh.material_ids.resize(num_faces); mesh.facevarying_normals.resize(num_faces * 3 * 3); mesh.facevarying_uvs.resize(num_faces * 3 * 2); mesh.vertices.resize(num_faces * 3 * 3); for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { // reorder vertices. may create duplicated vertices. size_t f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); size_t f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); size_t f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); mesh.vertices[9 * f + 0] = scale * attrib.vertices[3 * f0 + 0]; mesh.vertices[9 * f + 1] = scale * attrib.vertices[3 * f0 + 1]; mesh.vertices[9 * f + 2] = scale * attrib.vertices[3 * f0 + 2]; mesh.vertices[9 * f + 3] = scale * attrib.vertices[3 * f1 + 0]; mesh.vertices[9 * f + 4] = scale * attrib.vertices[3 * f1 + 1]; mesh.vertices[9 * f + 5] = scale * attrib.vertices[3 * f1 + 2]; mesh.vertices[9 * f + 6] = scale * attrib.vertices[3 * f2 + 0]; mesh.vertices[9 * f + 7] = scale * attrib.vertices[3 * f2 + 1]; mesh.vertices[9 * f + 8] = scale * attrib.vertices[3 * f2 + 2]; mesh.faces[3 * f + 0] = static_cast(3 * f + 0); mesh.faces[3 * f + 1] = static_cast(3 * f + 1); mesh.faces[3 * f + 2] = static_cast(3 * f + 2); mesh.material_ids[f] = static_cast(shapes[i].mesh.material_ids[f]); } if (attrib.normals.size() > 0) { for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { size_t f0, f1, f2; f0 = size_t(shapes[i].mesh.indices[3 * f + 0].normal_index); f1 = size_t(shapes[i].mesh.indices[3 * f + 1].normal_index); f2 = size_t(shapes[i].mesh.indices[3 * f + 2].normal_index); if (f0 > 0 && f1 > 0 && f2 > 0) { float n0[3], n1[3], n2[3]; n0[0] = attrib.normals[3 * f0 + 0]; n0[1] = attrib.normals[3 * f0 + 1]; n0[2] = attrib.normals[3 * f0 + 2]; n1[0] = attrib.normals[3 * f1 + 0]; n1[1] = attrib.normals[3 * f1 + 1]; n1[2] = attrib.normals[3 * f1 + 2]; n2[0] = attrib.normals[3 * f2 + 0]; n2[1] = attrib.normals[3 * f2 + 1]; n2[2] = attrib.normals[3 * f2 + 2]; mesh.facevarying_normals[3 * (3 * f + 0) + 0] = n0[0]; mesh.facevarying_normals[3 * (3 * f + 0) + 1] = n0[1]; mesh.facevarying_normals[3 * (3 * f + 0) + 2] = n0[2]; mesh.facevarying_normals[3 * (3 * f + 1) + 0] = n1[0]; mesh.facevarying_normals[3 * (3 * f + 1) + 1] = n1[1]; mesh.facevarying_normals[3 * (3 * f + 1) + 2] = n1[2]; mesh.facevarying_normals[3 * (3 * f + 2) + 0] = n2[0]; mesh.facevarying_normals[3 * (3 * f + 2) + 1] = n2[1]; mesh.facevarying_normals[3 * (3 * f + 2) + 2] = n2[2]; } else { // face contains invalid normal index. calc geometric normal. f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); float3 v0, v1, v2; v0[0] = attrib.vertices[3 * f0 + 0]; v0[1] = attrib.vertices[3 * f0 + 1]; v0[2] = attrib.vertices[3 * f0 + 2]; v1[0] = attrib.vertices[3 * f1 + 0]; v1[1] = attrib.vertices[3 * f1 + 1]; v1[2] = attrib.vertices[3 * f1 + 2]; v2[0] = attrib.vertices[3 * f2 + 0]; v2[1] = attrib.vertices[3 * f2 + 1]; v2[2] = attrib.vertices[3 * f2 + 2]; float3 N; CalcNormal(N, v0, v1, v2); mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2]; mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2]; mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2]; } } } else { // calc geometric normal for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { size_t f0, f1, f2; f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index); f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index); f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index); float3 v0, v1, v2; v0[0] = attrib.vertices[3 * f0 + 0]; v0[1] = attrib.vertices[3 * f0 + 1]; v0[2] = attrib.vertices[3 * f0 + 2]; v1[0] = attrib.vertices[3 * f1 + 0]; v1[1] = attrib.vertices[3 * f1 + 1]; v1[2] = attrib.vertices[3 * f1 + 2]; v2[0] = attrib.vertices[3 * f2 + 0]; v2[1] = attrib.vertices[3 * f2 + 1]; v2[2] = attrib.vertices[3 * f2 + 2]; float3 N; CalcNormal(N, v0, v1, v2); mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2]; mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2]; mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0]; mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1]; mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2]; } } if (attrib.texcoords.size() > 0) { for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { size_t f0, f1, f2; f0 = size_t(shapes[i].mesh.indices[3 * f + 0].texcoord_index); f1 = size_t(shapes[i].mesh.indices[3 * f + 1].texcoord_index); f2 = size_t(shapes[i].mesh.indices[3 * f + 2].texcoord_index); if (f0 > 0 && f1 > 0 && f2 > 0) { float3 n0, n1, n2; n0[0] = attrib.texcoords[2 * f0 + 0]; n0[1] = attrib.texcoords[2 * f0 + 1]; n1[0] = attrib.texcoords[2 * f1 + 0]; n1[1] = attrib.texcoords[2 * f1 + 1]; n2[0] = attrib.texcoords[2 * f2 + 0]; n2[1] = attrib.texcoords[2 * f2 + 1]; mesh.facevarying_uvs[2 * (3 * f + 0) + 0] = n0[0]; mesh.facevarying_uvs[2 * (3 * f + 0) + 1] = n0[1]; mesh.facevarying_uvs[2 * (3 * f + 1) + 0] = n1[0]; mesh.facevarying_uvs[2 * (3 * f + 1) + 1] = n1[1]; mesh.facevarying_uvs[2 * (3 * f + 2) + 0] = n2[0]; mesh.facevarying_uvs[2 * (3 * f + 2) + 1] = n2[1]; } } } // Compute pivot translation and add offset to the vertices. float bmin[3], bmax[3]; ComputeBoundingBoxOfMesh(bmin, bmax, mesh); float bcenter[3]; bcenter[0] = 0.5f * (bmax[0] - bmin[0]) + bmin[0]; bcenter[1] = 0.5f * (bmax[1] - bmin[1]) + bmin[1]; bcenter[2] = 0.5f * (bmax[2] - bmin[2]) + bmin[2]; for (size_t v = 0; v < mesh.vertices.size() / 3; v++) { mesh.vertices[3 * v + 0] -= bcenter[0]; mesh.vertices[3 * v + 1] -= bcenter[1]; mesh.vertices[3 * v + 2] -= bcenter[2]; } mesh.pivot_xform[0][0] = 1.0f; mesh.pivot_xform[0][1] = 0.0f; mesh.pivot_xform[0][2] = 0.0f; mesh.pivot_xform[0][3] = 0.0f; mesh.pivot_xform[1][0] = 0.0f; mesh.pivot_xform[1][1] = 1.0f; mesh.pivot_xform[1][2] = 0.0f; mesh.pivot_xform[1][3] = 0.0f; mesh.pivot_xform[2][0] = 0.0f; mesh.pivot_xform[2][1] = 0.0f; mesh.pivot_xform[2][2] = 1.0f; mesh.pivot_xform[2][3] = 0.0f; mesh.pivot_xform[3][0] = bcenter[0]; mesh.pivot_xform[3][1] = bcenter[1]; mesh.pivot_xform[3][2] = bcenter[2]; mesh.pivot_xform[3][3] = 1.0f; meshes->push_back(mesh); } // material_t -> Material and Texture out_materials->resize(materials.size()); out_textures->resize(0); for (size_t i = 0; i < materials.size(); i++) { (*out_materials)[i].diffuse[0] = materials[i].diffuse[0]; (*out_materials)[i].diffuse[1] = materials[i].diffuse[1]; (*out_materials)[i].diffuse[2] = materials[i].diffuse[2]; (*out_materials)[i].specular[0] = materials[i].specular[0]; (*out_materials)[i].specular[1] = materials[i].specular[1]; (*out_materials)[i].specular[2] = materials[i].specular[2]; (*out_materials)[i].id = int(i); // map_Kd (*out_materials)[i].diffuse_texid = LoadTexture(materials[i].diffuse_texname, out_textures); // map_Ks (*out_materials)[i].specular_texid = LoadTexture(materials[i].specular_texname, out_textures); } return true; } } // namespace example