| #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 <iostream> |
| |
| #ifdef NANOSG_USE_CXX11 |
| #include <unordered_map> |
| #else |
| #include <map> |
| #endif |
| |
| #define USE_TEX_CACHE 1 |
| |
| namespace example { |
| |
| typedef nanort::real3<float> 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<std::string, int> hashed_tex; |
| #else |
| static std::map<std::string, int> 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<Texture> *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<float> &mesh) { |
| bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max(); |
| bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::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<Mesh<float> > *meshes, |
| std::vector<Material> *out_materials, |
| std::vector<Texture> *out_textures) { |
| tinyobj::attrib_t attrib; |
| std::vector<tinyobj::shape_t> shapes; |
| std::vector<tinyobj::material_t> materials; |
| std::string warn; |
| 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, &warn, &err, filename.c_str(), |
| basepath, /* triangulate */ true); |
| |
| // auto t_end = std::chrono::system_clock::now(); |
| // std::chrono::duration<double, std::milli> ms = t_end - t_start; |
| |
| if (!warn.empty()) { |
| std::cout << warn << std::endl; |
| } |
| |
| 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<float> 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<unsigned int>(3 * f + 0); |
| mesh.faces[3 * f + 1] = static_cast<unsigned int>(3 * f + 1); |
| mesh.faces[3 * f + 2] = static_cast<unsigned int>(3 * f + 2); |
| |
| mesh.material_ids[f] = |
| static_cast<unsigned int>(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 |