From 1544f0c8650d89df995fbc91b7ed5751b5e2b7e7 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Wed, 11 Sep 2013 13:02:37 +0900 Subject: [PATCH] Parse ior and transmittance of material parameter. Add .obj sticher example. --- examples/obj_sticher/obj_sticher.cc | 82 ++++++++++++++++ examples/obj_sticher/obj_writer.cc | 139 ++++++++++++++++++++++++++++ examples/obj_sticher/obj_writer.h | 9 ++ examples/obj_sticher/premake4.lua | 38 ++++++++ test.cc | 1 + tiny_obj_loader.cc | 17 +++- tiny_obj_loader.h | 1 + 7 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 examples/obj_sticher/obj_sticher.cc create mode 100644 examples/obj_sticher/obj_writer.cc create mode 100644 examples/obj_sticher/obj_writer.h create mode 100644 examples/obj_sticher/premake4.lua diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc new file mode 100644 index 0000000..296d384 --- /dev/null +++ b/examples/obj_sticher/obj_sticher.cc @@ -0,0 +1,82 @@ +// +// Stiches multiple .obj files into one .obj. +// +#include "../../tiny_obj_loader.h" +#include "obj_writer.h" + +#include +#include + +typedef std::vector Shape; + +void +StichObjs( + std::vector& out, + const std::vector& shapes) +{ + int numShapes = 0; + for (size_t i = 0; i < shapes.size(); i++) { + numShapes += (int)shapes[i].size(); + } + + printf("Total # of shapes = %d\n", numShapes); + + size_t face_offset = 0; + for (size_t i = 0; i < shapes.size(); i++) { + for (size_t k = 0; k < shapes[i].size(); k++) { + + std::string new_name = shapes[i][k].name; + // Add suffix + char buf[1024]; + sprintf(buf, "_%04d", (int)i); + new_name += std::string(buf); + + printf("shape[%ld][%ld].name = %s\n", i, k, shapes[i][k].name.c_str()); + assert((shapes[i][k].mesh.indices.size() % 3) == 0); + assert((shapes[i][k].mesh.positions.size() % 3) == 0); + + tinyobj::shape_t new_shape = shapes[i][k]; + new_shape.name = new_name; + printf("shape[%ld][%ld].new_name = %s\n", i, k, new_shape.name.c_str()); + + out.push_back(new_shape); + } + } +} + +int +main( + int argc, + char **argv) +{ + if (argc < 3) { + printf("Usage: obj_sticher input0.obj input1.obj ... output.obj\n"); + exit(1); + } + + int num_objfiles = argc - 2; + std::string out_filename = std::string(argv[argc-1]); // last element + + std::vector shapes; + shapes.resize(num_objfiles); + + for (int i = 0; i < num_objfiles; i++) { + std::cout << "Loading " << argv[i+1] << " ... " << std::flush; + + std::string err = tinyobj::LoadObj(shapes[i], argv[i+1]); + if (!err.empty()) { + std::cerr << err << std::endl; + exit(1); + } + + std::cout << "DONE." << std::endl; + } + + std::vector out; + StichObjs(out, shapes); + + bool ret = WriteObj(out_filename, out); + assert(ret); + + return 0; +} diff --git a/examples/obj_sticher/obj_writer.cc b/examples/obj_sticher/obj_writer.cc new file mode 100644 index 0000000..67af93e --- /dev/null +++ b/examples/obj_sticher/obj_writer.cc @@ -0,0 +1,139 @@ +// +// Simple wavefront .obj writer +// +#include "obj_writer.h" + +static std::string GetFileBasename(const std::string& FileName) +{ + if(FileName.find_last_of(".") != std::string::npos) + return FileName.substr(0, FileName.find_last_of(".")); + return ""; +} + +bool WriteMat(const std::string& filename, std::vector shapes) { + FILE* fp = fopen(filename.c_str(), "w"); + if (!fp) { + fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); + return false; + } + + std::map mtl_table; + + for (size_t i = 0; i < shapes.size(); i++) { + mtl_table[shapes[i].material.name] = shapes[i].material; + } + + for (std::map::iterator it = mtl_table.begin(); it != mtl_table.end(); it++) { + + tinyobj::material_t mat = it->second; + + fprintf(fp, "newmtl %s\n", mat.name.c_str()); + fprintf(fp, "Ka %f %f %f\n", mat.ambient[0], mat.ambient[1], mat.ambient[2]); + fprintf(fp, "Kd %f %f %f\n", mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]); + fprintf(fp, "Ks %f %f %f\n", mat.specular[0], mat.specular[1], mat.specular[2]); + fprintf(fp, "Kt %f %f %f\n", mat.transmittance[0], mat.specular[1], mat.specular[2]); + fprintf(fp, "Ke %f %f %f\n", mat.emission[0], mat.emission[1], mat.emission[2]); + fprintf(fp, "Ns %f\n", mat.shininess); + fprintf(fp, "Ni %f\n", mat.ior); + // @todo { texture } + } + + fclose(fp); + + return true; +} + +bool WriteObj(const std::string& filename, std::vector shapes) { + FILE* fp = fopen(filename.c_str(), "w"); + if (!fp) { + fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); + return false; + } + + std::string basename = GetFileBasename(filename); + std::string material_filename = basename + ".mtl"; + + int v_offset = 0; + int vn_offset = 0; + int vt_offset = 0; + + fprintf(fp, "mtllib %s\n", material_filename.c_str()); + + for (size_t i = 0; i < shapes.size(); i++) { + + bool has_vn = false; + bool has_vt = false; + + if (shapes[i].name.empty()) { + fprintf(fp, "g Unknown\n"); + } else { + fprintf(fp, "g %s\n", shapes[i].name.c_str()); + } + + if (!shapes[i].material.name.empty()) { + fprintf(fp, "usemtl %s\n", shapes[i].material.name.c_str()); + } + + // vtx + for (size_t k = 0; k < shapes[i].mesh.positions.size() / 3; k++) { + fprintf(fp, "v %f %f %f\n", + shapes[i].mesh.positions[3*k+0], + shapes[i].mesh.positions[3*k+1], + shapes[i].mesh.positions[3*k+2]); + } + + // normal + for (size_t k = 0; k < shapes[i].mesh.normals.size() / 3; k++) { + fprintf(fp, "vn %f %f %f\n", + shapes[i].mesh.normals[3*k+0], + shapes[i].mesh.normals[3*k+1], + shapes[i].mesh.normals[3*k+2]); + } + if (shapes[i].mesh.normals.size() > 0) has_vn = true; + + // texcoord + for (size_t k = 0; k < shapes[i].mesh.texcoords.size() / 2; k++) { + fprintf(fp, "vt %f %f\n", + shapes[i].mesh.texcoords[2*k+0], + shapes[i].mesh.texcoords[2*k+1]); + } + if (shapes[i].mesh.texcoords.size() > 0) has_vt = true; + + // face + for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { + + // Face index is 1-base. + int v0 = shapes[i].mesh.indices[3*k+0] + 1 + v_offset; + int v1 = shapes[i].mesh.indices[3*k+1] + 1 + v_offset; + int v2 = shapes[i].mesh.indices[3*k+2] + 1 + v_offset; + + if (has_vn && has_vt) { + fprintf(fp, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", + v0, v0, v0, v1, v1, v1, v2, v2, v2); + } else if (has_vn && !has_vt) { + fprintf(fp, "f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2); + } else if (!has_vn && has_vt) { + fprintf(fp, "f %d/%d %d/%d %d/%d\n", v0, v0, v1, v1, v2, v2); + } else { + fprintf(fp, "f %d %d %d\n", v0, v1, v2); + } + + } + + v_offset += shapes[i].mesh.positions.size() / 3; + vn_offset += shapes[i].mesh.normals.size() / 3; + vt_offset += shapes[i].mesh.texcoords.size() / 2; + + } + + fclose(fp); + + // + // Write material file + // + bool ret = WriteMat(material_filename, shapes); + + return ret; +} + + diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h new file mode 100644 index 0000000..e31e0e4 --- /dev/null +++ b/examples/obj_sticher/obj_writer.h @@ -0,0 +1,9 @@ +#ifndef __OBJ_WRITER_H__ +#define __OBJ_WRITER_H__ + +#include "../../tiny_obj_loader.h" + +extern bool WriteObj(const std::string& filename, std::vector shapes); + + +#endif // __OBJ_WRITER_H__ diff --git a/examples/obj_sticher/premake4.lua b/examples/obj_sticher/premake4.lua new file mode 100644 index 0000000..9c2deb6 --- /dev/null +++ b/examples/obj_sticher/premake4.lua @@ -0,0 +1,38 @@ +lib_sources = { + "../../tiny_obj_loader.cc" +} + +sources = { + "obj_sticher.cc", + "obj_writer.cc", + } + +-- premake4.lua +solution "ObjStickerSolution" + configurations { "Release", "Debug" } + + if (os.is("windows")) then + platforms { "x32", "x64" } + else + platforms { "native", "x32", "x64" } + end + + includedirs { + "../../" + } + + -- A project defines one build target + project "obj_sticher" + kind "ConsoleApp" + language "C++" + files { lib_sources, sources } + + configuration "Debug" + defines { "DEBUG" } -- -DDEBUG + flags { "Symbols" } + targetname "obj_sticher_debug" + + configuration "Release" + -- defines { "NDEBUG" } -- -NDEBUG + flags { "Symbols", "Optimize" } + targetname "obj_sticher" diff --git a/test.cc b/test.cc index 7148135..578e96f 100644 --- a/test.cc +++ b/test.cc @@ -46,6 +46,7 @@ TestLoadObj( printf(" material.Tr = (%f, %f ,%f)\n", shapes[i].material.transmittance[0], shapes[i].material.transmittance[1], shapes[i].material.transmittance[2]); printf(" material.Ke = (%f, %f ,%f)\n", shapes[i].material.emission[0], shapes[i].material.emission[1], shapes[i].material.emission[2]); printf(" material.Ns = %f\n", shapes[i].material.shininess); + printf(" material.Ni = %f\n", shapes[i].material.ior); printf(" material.map_Ka = %s\n", shapes[i].material.ambient_texname.c_str()); printf(" material.map_Kd = %s\n", shapes[i].material.diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", shapes[i].material.specular_texname.c_str()); diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index 0cc9e29..7894e70 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,8 @@ // // +// version 0.9.6: Support Ni(index of refraction) mtl parameter. +// Parse transmittance material parameter correctly. // version 0.9.5: Parse multiple group name. // Add support of specifying the base path to load material file. // version 0.9.4: Initial suupport of group tag(g) @@ -372,14 +374,21 @@ std::string LoadMtl ( continue; } - // specular + // transmittance if (token[0] == 'K' && token[1] == 't' && isSpace((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; + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { + token += 2; + material.ior = parseFloat(token); continue; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index bc3ad2e..a5324eb 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -22,6 +22,7 @@ typedef struct float transmittance[3]; float emission[3]; float shininess; + float ior; // index of refraction std::string ambient_texname; std::string diffuse_texname;