diff --git a/README.md b/README.md index bb5418f..f0eadf3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Good for embedding .obj loader to your (global illumination) renderer ;-) What's new ---------- +* Sep 14, 2014 : Add support for multi-material per object/group. Thanks Mykhailo! * Mar 17, 2014 : Fixed trim newline bugs. Thanks ardneran! * Apr 29, 2014 : Add API to read .obj from std::istream. Good for reading compressed .obj or connecting to procedural primitive generator. Thanks burnse! * Apr 21, 2014 : Define default material if no material definition exists in .obj. Thanks YarmUI! @@ -36,6 +37,7 @@ TinyObjLoader is successfully used in ... * bullet3 https://github.com/erwincoumans/bullet3 * pbrt-v2 https://https://github.com/mmp/pbrt-v2 * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 +* Your project here! Features -------- @@ -67,24 +69,27 @@ Usage std::string inputfile = "cornell_box.obj"; std::vector shapes; + std::vector materials; - std::string err = tinyobj::LoadObj(shapes, inputfile.c_str()); + std::string err = tinyobj::LoadObj(shapes, materials, inputfile.c_str()); if (!err.empty()) { std::cerr << err << std::endl; exit(1); } - - std::cout << "# of shapes : " << shapes.size() << std::endl; + + std::cout << "# of shapes : " << shapes.size() << std::endl; + std::cout << "# of materials : " << materials.size() << std::endl; for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { - printf(" idx[%ld] = %d\n", f, shapes[i].mesh.indices[f]); + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); } - + printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); assert((shapes[i].mesh.positions.size() % 3) == 0); for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { @@ -93,22 +98,28 @@ Usage shapes[i].mesh.positions[3*v+1], shapes[i].mesh.positions[3*v+2]); } - - printf("shape[%ld].material.name = %s\n", i, shapes[i].material.name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", shapes[i].material.ambient[0], shapes[i].material.ambient[1], shapes[i].material.ambient[2]); - printf(" material.Kd = (%f, %f ,%f)\n", shapes[i].material.diffuse[0], shapes[i].material.diffuse[1], shapes[i].material.diffuse[2]); - printf(" material.Ks = (%f, %f ,%f)\n", shapes[i].material.specular[0], shapes[i].material.specular[1], shapes[i].material.specular[2]); - 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.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()); - printf(" material.map_Ns = %s\n", shapes[i].material.normal_texname.c_str()); - std::map::iterator it(shapes[i].material.unknown_parameter.begin()); - std::map::iterator itEnd(shapes[i].material.unknown_parameter.end()); + } + + for (size_t i = 0; i < materials.size(); i++) { + printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); + printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]); + printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]); + printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]); + printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]); + printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]); + printf(" material.Ns = %f\n", materials[i].shininess); + printf(" material.Ni = %f\n", materials[i].ior); + printf(" material.dissolve = %f\n", 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].normal_texname.c_str()); + std::map::const_iterator it(materials[i].unknown_parameter.begin()); + std::map::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"); } + diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index 1d230b8..1833216 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -10,11 +10,14 @@ #include typedef std::vector Shape; +typedef std::vector Material; void StichObjs( - std::vector& out, - const std::vector& shapes) + std::vector& out_shape, + std::vector& out_material, + const std::vector& shapes, + const std::vector& materials) { int numShapes = 0; for (size_t i = 0; i < shapes.size(); i++) { @@ -22,9 +25,11 @@ StichObjs( } printf("Total # of shapes = %d\n", numShapes); + int materialIdOffset = 0; 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; @@ -38,12 +43,26 @@ StichObjs( assert((shapes[i][k].mesh.positions.size() % 3) == 0); tinyobj::shape_t new_shape = shapes[i][k]; + // Add offset. + for (size_t f = 0; f < new_shape.mesh.material_ids.size(); f++) { + new_shape.mesh.material_ids[f] += materialIdOffset; + } + 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); + out_shape.push_back(new_shape); + } + + materialIdOffset += materials[i].size(); + } + + for (size_t i = 0; i < materials.size(); i++) { + for (size_t k = 0; k < materials[i].size(); k++) { + out_material.push_back(materials[i][k]); } } + } int @@ -60,12 +79,13 @@ main( std::string out_filename = std::string(argv[argc-1]); // last element std::vector shapes; + std::vector materials; 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]); + std::string err = tinyobj::LoadObj(shapes[i], materials[i], argv[i+1]); if (!err.empty()) { std::cerr << err << std::endl; exit(1); @@ -74,10 +94,11 @@ main( std::cout << "DONE." << std::endl; } - std::vector out; - StichObjs(out, shapes); + std::vector out_shape; + std::vector out_material; + StichObjs(out_shape, out_material, shapes, materials); - bool ret = WriteObj(out_filename, out); + bool ret = WriteObj(out_filename, out_shape, out_material); assert(ret); return 0; diff --git a/examples/obj_sticher/obj_writer.cc b/examples/obj_sticher/obj_writer.cc index f44d3f3..bb12457 100644 --- a/examples/obj_sticher/obj_writer.cc +++ b/examples/obj_sticher/obj_writer.cc @@ -11,22 +11,16 @@ static std::string GetFileBasename(const std::string& FileName) return ""; } -bool WriteMat(const std::string& filename, std::vector shapes) { +bool WriteMat(const std::string& filename, const std::vector& materials) { 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 < materials.size(); i++) { - 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; + tinyobj::material_t mat = materials[i]; 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]); @@ -44,7 +38,7 @@ bool WriteMat(const std::string& filename, std::vector shapes) return true; } -bool WriteObj(const std::string& filename, std::vector shapes) { +bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials) { FILE* fp = fopen(filename.c_str(), "w"); if (!fp) { fprintf(stderr, "Failed to open file [ %s ] for write.\n", filename.c_str()); @@ -57,6 +51,7 @@ bool WriteObj(const std::string& filename, std::vector shapes) int v_offset = 0; int vn_offset = 0; int vt_offset = 0; + int prev_material_id = -1; fprintf(fp, "mtllib %s\n", material_filename.c_str()); @@ -71,9 +66,9 @@ bool WriteObj(const std::string& filename, std::vector shapes) 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()); - } + //if (!shapes[i].material.name.empty()) { + // fprintf(fp, "usemtl %s\n", shapes[i].material.name.c_str()); + //} // facevarying vtx for (size_t k = 0; k < shapes[i].mesh.indices.size() / 3; k++) { @@ -124,6 +119,13 @@ bool WriteObj(const std::string& filename, std::vector shapes) int v1 = (3*k + 1) + 1 + v_offset; int v2 = (3*k + 2) + 1 + v_offset; + int material_id = shapes[i].mesh.material_ids[k]; + if (material_id != prev_material_id) { + std::string material_name = materials[material_id].name; + fprintf(fp, "usemtl %s\n", material_name.c_str()); + prev_material_id = material_id; + } + 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); @@ -148,7 +150,7 @@ bool WriteObj(const std::string& filename, std::vector shapes) // // Write material file // - bool ret = WriteMat(material_filename, shapes); + bool ret = WriteMat(material_filename, materials); return ret; } diff --git a/examples/obj_sticher/obj_writer.h b/examples/obj_sticher/obj_writer.h index e31e0e4..00cd792 100644 --- a/examples/obj_sticher/obj_writer.h +++ b/examples/obj_sticher/obj_writer.h @@ -3,7 +3,7 @@ #include "../../tiny_obj_loader.h" -extern bool WriteObj(const std::string& filename, std::vector shapes); +extern bool WriteObj(const std::string& filename, const std::vector& shapes, const std::vector& materials); #endif // __OBJ_WRITER_H__ diff --git a/test.cc b/test.cc index 1eeec7a..1ad6d8c 100644 --- a/test.cc +++ b/test.cc @@ -14,8 +14,8 @@ static void PrintInfo(const std::vector& shapes, const std::ve for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); - printf("shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); + printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); assert((shapes[i].mesh.indices.size() % 3) == 0); for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]); @@ -29,28 +29,28 @@ static void PrintInfo(const std::vector& shapes, const std::ve shapes[i].mesh.positions[3*v+1], shapes[i].mesh.positions[3*v+2]); } - -#if 0 - printf("shape[%ld].material.name = %s\n", i, shapes[i].material.name.c_str()); - printf(" material.Ka = (%f, %f ,%f)\n", shapes[i].material.ambient[0], shapes[i].material.ambient[1], shapes[i].material.ambient[2]); - printf(" material.Kd = (%f, %f ,%f)\n", shapes[i].material.diffuse[0], shapes[i].material.diffuse[1], shapes[i].material.diffuse[2]); - printf(" material.Ks = (%f, %f ,%f)\n", shapes[i].material.specular[0], shapes[i].material.specular[1], shapes[i].material.specular[2]); - 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.dissolve = %f\n", shapes[i].material.dissolve); - printf(" material.illum = %d\n", shapes[i].material.illum); - 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()); - printf(" material.map_Ns = %s\n", shapes[i].material.normal_texname.c_str()); - std::map::const_iterator it(shapes[i].material.unknown_parameter.begin()); - std::map::const_iterator itEnd(shapes[i].material.unknown_parameter.end()); + } + + for (size_t i = 0; i < materials.size(); i++) { + printf("material[%ld].name = %s\n", i, materials[i].name.c_str()); + printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]); + printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]); + printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]); + printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]); + printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]); + printf(" material.Ns = %f\n", materials[i].shininess); + printf(" material.Ni = %f\n", materials[i].ior); + printf(" material.dissolve = %f\n", 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].normal_texname.c_str()); + std::map::const_iterator it(materials[i].unknown_parameter.begin()); + std::map::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()); } -#endif printf("\n"); } } @@ -77,7 +77,6 @@ TestLoadObj( } -#if 0 // @todo static bool TestStreamLoadObj() { @@ -152,9 +151,10 @@ std::string matStream( virtual ~MaterialStringStreamReader() {} virtual std::string operator() ( const std::string& matId, - std::map& matMap) + std::vector& materials, + std::map& matMap) { - return LoadMtl(matMap, m_matSStream); + return LoadMtl(matMap, materials, m_matSStream); } private: @@ -163,18 +163,18 @@ std::string matStream( MaterialStringStreamReader matSSReader(matStream); std::vector shapes; - std::string err = tinyobj::LoadObj(shapes, objStream, matSSReader); + std::vector materials; + std::string err = tinyobj::LoadObj(shapes, materials, objStream, matSSReader); if (!err.empty()) { std::cerr << err << std::endl; return false; } - PrintInfo(shapes); + PrintInfo(shapes, materials); return true; } -#endif int main( @@ -189,9 +189,9 @@ main( } assert(true == TestLoadObj(argv[1], basepath)); } else { - assert(true == TestLoadObj("cornell_box.obj")); - assert(true == TestLoadObj("cube.obj")); - //assert(true == TestStreamLoadObj()); @todo + //assert(true == TestLoadObj("cornell_box.obj")); + //assert(true == TestLoadObj("cube.obj")); + assert(true == TestStreamLoadObj()); } return 0; diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index d650688..e2f5257 100755 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -5,6 +5,7 @@ // // +// version 0.9.7: Support multi-materials(per-face material ID) per object/grou . // version 0.9.6: Support Ni(index of refraction) mtl parameter. // Parse transmittance material parameter correctly. // version 0.9.5: Parse multiple group name. @@ -663,7 +664,7 @@ std::string LoadObj( shape = shape_t(); - material = -1; + //material = -1; faceGroup.clear(); std::vector names; @@ -694,7 +695,7 @@ std::string LoadObj( shapes.push_back(shape); } - material = -1; + //material = -1; faceGroup.clear(); shape = shape_t(); diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 1fe36d7..a58d7be 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -47,8 +47,6 @@ typedef struct { std::string name; mesh_t mesh; - //std::vector< std::pair > submeshes; - //std::vector< int > materials; } shape_t; class MaterialReader