22 Commits

Author SHA1 Message Date
Syoyo Fujita
f37fed32f3 Merge pull request #196 from jasjuang/master
Use CMake built-in variable BUILD_SHARED_LIBS instead of custom TINYOBJLOADER_COMPILATION_SHARED
2019-01-05 15:52:55 +09:00
jasjuang
85d92bb5cb use cmake default variable BUILD_SHARED_LIBS instead of custom TINYOBJLOADER_COMPILATION_SHARED 2019-01-04 11:06:27 -08:00
Syoyo Fujita
40d00fd935 Merge pull request #195 from Neptilo/master
Fix crash when reading mtllib command
2019-01-04 22:13:05 +09:00
Neptilo
7712f1bebd Fix crash when reading mtllib command
The code was assuming the mtllib command to be near the beginning of the file, and therefore to always be read by the first thread.
2019-01-04 13:57:34 +01:00
Syoyo Fujita
e96e994355 Merge pull request #194 from jasjuang/master
namespace for cmake export
2019-01-04 12:33:19 +09:00
jasjuang
8f16866c37 namespace for cmake export 2019-01-03 19:26:00 -08:00
Syoyo Fujita
5e668b398a Merge pull request #193 from scpeters/patch-1
tiny_obj_loader.h: initialize pad_ in constructor
2018-12-15 01:30:19 +09:00
Steven Peters
df3bb6f8e8 tiny_obj_loader.h: fix order of variables 2018-12-13 10:49:18 -08:00
Steven Peters
546aa09d8d tiny_obj_loader.h: initialize pad_ in constructor 2018-12-11 15:09:56 -08:00
Syoyo Fujita
aa90b5466f Merge pull request #192 from basicNew/ChangeLoopTerminationConstraint
Change triangulation loop termination constraint
2018-12-08 17:57:06 +09:00
Andres Fortier
b404f3af67 Change loop termination constraint 2018-12-07 17:06:38 -03:00
Syoyo Fujita
a957ebe002 Fix some coments.
Bump version 1.4.0
2018-12-01 13:32:05 +09:00
Syoyo Fujita
d793bfb405 Merge pull request #191 from sinisterchipmunk/initialize_unparsed_texopts
Properly initialize all texture_option_t's, not just parsed ones
2018-12-01 13:25:41 +09:00
Colin MacKenzie IV
0ab0146296 Update function declaration 2018-11-30 15:27:38 -05:00
Colin MacKenzie IV
20d122f305 Properly initialize all texture_option_t's, not just parsed ones 2018-11-30 15:14:07 -05:00
Syoyo Fujita
850d0ffdfa Merge pull request #190 from ukabuer/patch-1
Update README.md
2018-11-30 21:29:21 +09:00
UKABUER
80058fdcb0 Update README.md 2018-11-30 20:24:14 +08:00
Syoyo Fujita
1a7aea4ac1 Merge pull request #189 from mathue/patch-1
Fix of important memory leak in Python module
2018-11-29 01:31:03 +09:00
Martin Thümmel
178ef391c7 Fix of important memory leak in Python module
Dear all,
As described in the issue https://github.com/syoyo/tinyobjloader/issues/188#issue-385341218 there is a memory leak in the function pyLoadObj(). The reference count for all created Python objects must be decreased after they are fed into the lists and dictionaries. The only  exception from this is the function PyTuple_SetItem() in pyTupleFromfloat3(), which decreases the reference counter of the Python object *tuple automatically by calling this function. In all other cases like PyList_Insert(), the references are only borrowed and not decreased. Therefore, each created Python object will remain in the heap, since there is one reference to it left in the counter.

These facts are explained in https://docs.python.org/2/c-api/intro.html#reference-counts in detail.

Best regards
Martin.
P.S. sorry, that i did not put that much effort into a more readable code and just inserted the Py_DECREF() function at every necessary position.
2018-11-28 17:16:12 +01:00
Syoyo Fujita
59b4d7ccef Merge pull request #187 from Neptilo/master
Fix parsing double with negative exponent
2018-11-06 00:28:10 +09:00
Victor Ripplinger
6f990e2b6c Fix parsing double with negative exponent 2018-11-05 15:17:34 +01:00
Syoyo Fujita
7fb5056a53 Make ParseTextureNameAndOption function public. 2018-10-19 15:29:31 +09:00
6 changed files with 240 additions and 169 deletions

View File

@@ -46,15 +46,13 @@ set(TINYOBJLOADER_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
set(TINYOBJLOADER_RUNTIME_DIR ${CMAKE_INSTALL_BINDIR}) set(TINYOBJLOADER_RUNTIME_DIR ${CMAKE_INSTALL_BINDIR})
option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Example Loader Application" OFF) option(TINYOBJLOADER_BUILD_TEST_LOADER "Build Example Loader Application" OFF)
option(TINYOBJLOADER_COMPILATION_SHARED "Build as shared library" OFF)
if(TINYOBJLOADER_COMPILATION_SHARED) add_library(${LIBRARY_NAME} ${tinyobjloader-Source})
add_library(${LIBRARY_NAME} SHARED ${tinyobjloader-Source})
if(BUILD_SHARED_LIBS)
set_target_properties(${LIBRARY_NAME} PROPERTIES set_target_properties(${LIBRARY_NAME} PROPERTIES
SOVERSION ${TINYOBJLOADER_SOVERSION} SOVERSION ${TINYOBJLOADER_SOVERSION}
) )
else()
add_library(${LIBRARY_NAME} STATIC ${tinyobjloader-Source})
endif() endif()
set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${TINYOBJLOADER_VERSION}) set_target_properties(${LIBRARY_NAME} PROPERTIES VERSION ${TINYOBJLOADER_VERSION})
@@ -120,6 +118,8 @@ install(TARGETS
) )
install(EXPORT install(EXPORT
${PROJECT_NAME}-targets ${PROJECT_NAME}-targets
NAMESPACE
tinyobjloader::
DESTINATION DESTINATION
${TINYOBJLOADER_CMAKE_DIR} ${TINYOBJLOADER_CMAKE_DIR}
) )

View File

@@ -204,9 +204,10 @@ std::string inputfile = "cornell_box.obj";
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err; std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, inputfile.c_str()); bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str());
if (!err.empty()) { // `err` may contain warning message. if (!err.empty()) { // `err` may contain warning message.
std::cerr << err << std::endl; std::cerr << err << std::endl;

View File

@@ -605,19 +605,9 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
} }
assemble : assemble :
*result = (sign == '+' ? 1 : -1) *
{ (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
// = pow(5.0, exponent); : mantissa);
double a = 1.0;
for (int i = 0; i < exponent; i++) {
a = a * 5.0;
}
*result =
//(sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
(sign == '+' ? 1 : -1) * (mantissa * a) *
static_cast<double>(1ULL << exponent); // 5.0^exponent * 2^exponent
}
return true; return true;
fail: fail:
return false; return false;
@@ -1424,8 +1414,9 @@ bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
} }
if (command.type == COMMAND_MTLLIB) { if (command.type == COMMAND_MTLLIB) {
// Save the indices of the `mtllib` command in `commands` to easily find it later
mtllib_t_index = t; mtllib_t_index = t;
mtllib_i_index = commands->size(); mtllib_i_index = commands[t].size();
} }
commands[t].emplace_back(std::move(command)); commands[t].emplace_back(std::move(command));

View File

@@ -18,11 +18,9 @@ typedef std::vector<int> vecti;
PyObject* pyTupleFromfloat3(float array[3]) { PyObject* pyTupleFromfloat3(float array[3]) {
int i; int i;
PyObject* tuple = PyTuple_New(3); PyObject* tuple = PyTuple_New(3);
for (i = 0; i <= 2; i++) { for (i = 0; i <= 2; i++) {
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i])); PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
} }
return tuple; return tuple;
} }
@@ -30,32 +28,27 @@ extern "C" {
static PyObject* pyLoadObj(PyObject* self, PyObject* args) { static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj; PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
char const* current_name; char const* current_name;
char const* filename; char const* filename;
vectd vect; vectd vect;
std::vector<tinyobj::index_t> indices; std::vector<tinyobj::index_t> indices;
std::vector<unsigned char> face_verts; std::vector<unsigned char> face_verts;
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials; std::vector<tinyobj::material_t> materials;
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL; if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
std::string err; std::string err, warn;
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename); tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename);
pyshapes = PyDict_New(); pyshapes = PyDict_New();
pymaterials = PyDict_New(); pymaterials = PyDict_New();
pymaterial_indices = PyList_New(0); pymaterial_indices = PyList_New(0);
rtndict = PyDict_New(); rtndict = PyDict_New();
attribobj = PyDict_New(); attribobj = PyDict_New();
for (int i = 0; i <= 2; i++) { for (int i = 0; i <= 2; i++) {
current = PyList_New(0); current = PyList_New(0);
switch (i) { switch (i) {
case 0: case 0:
current_name = "vertices"; current_name = "vertices";
@@ -72,124 +65,137 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
} }
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) { for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it)); PyObject* value = PyFloat_FromDouble(*it);
PyList_Insert(current, it - vect.begin(), value);
Py_DECREF(value);
} }
PyDict_SetItemString(attribobj, current_name, current); PyDict_SetItemString(attribobj, current_name, current);
Py_DECREF(current);
} }
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin(); for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin(); shape != shapes.end(); shape++) {
shape != shapes.end(); shape++) {
meshobj = PyDict_New(); meshobj = PyDict_New();
tinyobj::mesh_t cm = (*shape).mesh; tinyobj::mesh_t cm = (*shape).mesh;
{ {
current = PyList_New(0); current = PyList_New(0);
for (size_t i = 0; i < cm.indices.size(); i++) { for (size_t i = 0; i < cm.indices.size(); i++) {
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx, // Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
// ... PyObject* value = PyLong_FromLong(cm.indices[i].vertex_index);
PyList_Insert(current, 3 * i + 0, PyList_Insert(current, 3 * i + 0, value);
PyLong_FromLong(cm.indices[i].vertex_index)); Py_DECREF(value);
PyList_Insert(current, 3 * i + 1, value = PyLong_FromLong(cm.indices[i].normal_index);
PyLong_FromLong(cm.indices[i].normal_index)); PyList_Insert(current, 3 * i + 1, value);
PyList_Insert(current, 3 * i + 2, Py_DECREF(value);
PyLong_FromLong(cm.indices[i].texcoord_index)); value = PyLong_FromLong(cm.indices[i].texcoord_index);
PyList_Insert(current, 3 * i + 2, value);
Py_DECREF(value);
} }
PyDict_SetItemString(meshobj, "indices", current); PyDict_SetItemString(meshobj, "indices", current);
Py_DECREF(current);
} }
{ {
current = PyList_New(0); current = PyList_New(0);
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) { for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
// Widen data type to long. // Widen data type to long.
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i])); PyObject* value = PyLong_FromLong(cm.num_face_vertices[i]);
PyList_Insert(current, i, value);
Py_DECREF(value);
} }
PyDict_SetItemString(meshobj, "num_face_vertices", current); PyDict_SetItemString(meshobj, "num_face_vertices", current);
Py_DECREF(current);
} }
{ {
current = PyList_New(0); current = PyList_New(0);
for (size_t i = 0; i < cm.material_ids.size(); i++) { for (size_t i = 0; i < cm.material_ids.size(); i++) {
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i])); PyObject* value = PyLong_FromLong(cm.material_ids[i]);
PyList_Insert(current, i, value);
Py_DECREF(value);
} }
PyDict_SetItemString(meshobj, "material_ids", current); PyDict_SetItemString(meshobj, "material_ids", current);
Py_DECREF(current);
} }
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj); PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
Py_DECREF(meshobj);
} }
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin(); for (std::vector<tinyobj::material_t>::iterator mat = materials.begin(); mat != materials.end(); mat++) {
mat != materials.end(); mat++) {
PyObject* matobj = PyDict_New(); PyObject* matobj = PyDict_New();
PyObject* unknown_parameter = PyDict_New(); PyObject* unknown_parameter = PyDict_New();
for (std::map<std::string, std::string>::iterator p = for (std::map<std::string, std::string>::iterator p = mat->unknown_parameter.begin(); p != mat->unknown_parameter.end(); ++p) {
mat->unknown_parameter.begin(); PyObject* value = PyUnicode_FromString(p->second.c_str());
p != mat->unknown_parameter.end(); ++p) { PyDict_SetItemString(unknown_parameter, p->first.c_str(), value);
PyDict_SetItemString(unknown_parameter, p->first.c_str(), Py_DECREF(value);
PyUnicode_FromString(p->second.c_str()));
} }
PyDict_SetItemString(matobj, "shininess", PyObject* value = PyFloat_FromDouble(mat->shininess);
PyFloat_FromDouble(mat->shininess)); PyDict_SetItemString(matobj, "shininess", value);
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior)); Py_DECREF(value);
PyDict_SetItemString(matobj, "dissolve", value = PyFloat_FromDouble(mat->ior);
PyFloat_FromDouble(mat->dissolve)); PyDict_SetItemString(matobj, "ior", value);
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum)); Py_DECREF(value);
PyDict_SetItemString(matobj, "ambient_texname", value = PyFloat_FromDouble(mat->dissolve);
PyUnicode_FromString(mat->ambient_texname.c_str())); PyDict_SetItemString(matobj, "dissolve", value);
PyDict_SetItemString(matobj, "diffuse_texname", Py_DECREF(value);
PyUnicode_FromString(mat->diffuse_texname.c_str())); value = PyLong_FromLong(mat->illum);
PyDict_SetItemString(matobj, "specular_texname", PyDict_SetItemString(matobj, "illum", value);
PyUnicode_FromString(mat->specular_texname.c_str())); Py_DECREF(value);
PyDict_SetItemString( value = PyUnicode_FromString(mat->ambient_texname.c_str());
matobj, "specular_highlight_texname", PyDict_SetItemString(matobj, "ambient_texname", value);
PyUnicode_FromString(mat->specular_highlight_texname.c_str())); Py_DECREF(value);
PyDict_SetItemString(matobj, "bump_texname", value = PyUnicode_FromString(mat->diffuse_texname.c_str());
PyUnicode_FromString(mat->bump_texname.c_str())); PyDict_SetItemString(matobj, "diffuse_texname", value);
PyDict_SetItemString( Py_DECREF(value);
matobj, "displacement_texname", value = PyUnicode_FromString(mat->specular_texname.c_str());
PyUnicode_FromString(mat->displacement_texname.c_str())); PyDict_SetItemString(matobj, "specular_texname", value);
PyDict_SetItemString(matobj, "alpha_texname", Py_DECREF(value);
PyUnicode_FromString(mat->alpha_texname.c_str())); value = PyUnicode_FromString(mat->specular_highlight_texname.c_str());
PyDict_SetItemString(matobj, "specular_highlight_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->bump_texname.c_str());
PyDict_SetItemString(matobj, "bump_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->displacement_texname.c_str());
PyDict_SetItemString(matobj, "displacement_texname", value);
Py_DECREF(value);
value = PyUnicode_FromString(mat->alpha_texname.c_str());
PyDict_SetItemString(matobj, "alpha_texname", value);
Py_DECREF(value);
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient)); PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse)); PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
PyDict_SetItemString(matobj, "specular", PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3(mat->specular));
pyTupleFromfloat3(mat->specular)); PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3(mat->transmittance));
PyDict_SetItemString(matobj, "transmittance", PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3(mat->emission));
pyTupleFromfloat3(mat->transmittance));
PyDict_SetItemString(matobj, "emission",
pyTupleFromfloat3(mat->emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter); PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
Py_DECREF(unknown_parameter);
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj); PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str())); Py_DECREF(matobj);
value = PyUnicode_FromString(mat->name.c_str());
PyList_Append(pymaterial_indices, value);
Py_DECREF(value);
} }
PyDict_SetItemString(rtndict, "shapes", pyshapes); PyDict_SetItemString(rtndict, "shapes", pyshapes);
Py_DECREF(pyshapes);
PyDict_SetItemString(rtndict, "materials", pymaterials); PyDict_SetItemString(rtndict, "materials", pymaterials);
Py_DECREF(pymaterials);
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices); PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
Py_DECREF(pymaterial_indices);
PyDict_SetItemString(rtndict, "attribs", attribobj); PyDict_SetItemString(rtndict, "attribs", attribobj);
Py_DECREF(attribobj);
return rtndict; return rtndict;
} }
static PyMethodDef mMethods[] = { static PyMethodDef mMethods[] = {
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL} {"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
}; };
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader", static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader", NULL, -1, mMethods};
NULL, -1, mMethods};
PyMODINIT_FUNC PyInit_tinyobjloader(void) { PyMODINIT_FUNC PyInit_tinyobjloader(void) {
return PyModule_Create(&moduledef); return PyModule_Create(&moduledef);

View File

@@ -986,6 +986,49 @@ TEST_CASE("multiple-group-names", "[group]") {
// single white space. // single white space.
} }
TEST_CASE("initialize_all_texopts", "[ensure unparsed texopts are initialized to defaults]") {
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,
"../models/cornell_box.obj", gMtlBasePath, false);
REQUIRE(0 < materials.size());
#define REQUIRE_DEFAULT_TEXOPT(texopt) \
REQUIRE(tinyobj::TEXTURE_TYPE_NONE == texopt.type); \
REQUIRE(0.0 == texopt.brightness); \
REQUIRE(1.0 == texopt.contrast); \
REQUIRE(false == texopt.clamp); \
REQUIRE(true == texopt.blendu); \
REQUIRE(true == texopt.blendv); \
REQUIRE(1.0 == texopt.bump_multiplier); \
for (int j = 0; j < 3; j++) { \
REQUIRE(0.0 == texopt.origin_offset[j]); \
REQUIRE(1.0 == texopt.scale[j]); \
REQUIRE(0.0 == texopt.turbulence[j]); \
}
for (size_t i = 0; i < materials.size(); i++) {
REQUIRE_DEFAULT_TEXOPT(materials[i].ambient_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].diffuse_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].specular_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].specular_highlight_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].bump_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].displacement_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].alpha_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].reflection_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].roughness_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].metallic_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].sheen_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].emissive_texopt);
REQUIRE_DEFAULT_TEXOPT(materials[i].normal_texopt);
}
#undef REQUIRE_DEFAULT_TEXOPT
}
TEST_CASE("colorspace", "[Issue184]") { TEST_CASE("colorspace", "[Issue184]") {
tinyobj::attrib_t attrib; tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes; std::vector<tinyobj::shape_t> shapes;

View File

@@ -23,6 +23,8 @@ THE SOFTWARE.
*/ */
// //
// version 1.4.0 : Modifed ParseTextureNameAndOption API
// version 1.3.1 : Make ParseTextureNameAndOption API public
// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) // version 1.3.0 : Separate warning and error message(breaking API of LoadObj)
// version 1.2.3 : Added color space extension('-colorspace') to tex opts. // version 1.2.3 : Added color space extension('-colorspace') to tex opts.
// version 1.2.2 : Parse multiple group names. // version 1.2.2 : Parse multiple group names.
@@ -157,8 +159,8 @@ typedef struct {
real_t bump_multiplier; // -bm (for bump maps only, default 1.0) real_t bump_multiplier; // -bm (for bump maps only, default 1.0)
// extension // extension
std::string colorspace; // Explicitly specify color space of stored value. std::string colorspace; // Explicitly specify color space of stored texel
// Usually `sRGB` or `linear` (default empty). // value. Usually `sRGB` or `linear` (default empty).
} texture_option_t; } texture_option_t;
typedef struct { typedef struct {
@@ -386,6 +388,16 @@ void LoadMtl(std::map<std::string, int> *material_map,
std::vector<material_t> *materials, std::istream *inStream, std::vector<material_t> *materials, std::istream *inStream,
std::string *warning, std::string *err); std::string *warning, std::string *err);
///
/// Parse texture name and texture option for custom texture parameter through
/// material::unknown_parameter
///
/// @param[out] texname Parsed texture name
/// @param[out] texopt Parsed texopt
/// @param[in] linebuf Input string
///
bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
const char *linebuf);
} // namespace tinyobj } // namespace tinyobj
#endif // TINY_OBJ_LOADER_H_ #endif // TINY_OBJ_LOADER_H_
@@ -423,7 +435,7 @@ struct face_t {
int pad_; int pad_;
std::vector<vertex_index_t> vertex_indices; // face vertex indices. std::vector<vertex_index_t> vertex_indices; // face vertex indices.
face_t() : smoothing_group_id(0) {} face_t() : smoothing_group_id(0), pad_(0) {}
}; };
struct line_t { struct line_t {
@@ -891,37 +903,12 @@ static vertex_index_t parseRawTriple(const char **token) {
return vi; return vi;
} }
static bool ParseTextureNameAndOption(std::string *texname, bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt,
texture_option_t *texopt, const char *linebuf) {
const char *linebuf, const bool is_bump) {
// @todo { write more robust lexer and parser. } // @todo { write more robust lexer and parser. }
bool found_texname = false; bool found_texname = false;
std::string texture_name; std::string texture_name;
// Fill with default value for texopt.
if (is_bump) {
texopt->imfchan = 'l';
} else {
texopt->imfchan = 'm';
}
texopt->bump_multiplier = static_cast<real_t>(1.0);
texopt->clamp = false;
texopt->blendu = true;
texopt->blendv = true;
texopt->sharpness = static_cast<real_t>(1.0);
texopt->brightness = static_cast<real_t>(0.0);
texopt->contrast = static_cast<real_t>(1.0);
texopt->origin_offset[0] = static_cast<real_t>(0.0);
texopt->origin_offset[1] = static_cast<real_t>(0.0);
texopt->origin_offset[2] = static_cast<real_t>(0.0);
texopt->scale[0] = static_cast<real_t>(1.0);
texopt->scale[1] = static_cast<real_t>(1.0);
texopt->scale[2] = static_cast<real_t>(1.0);
texopt->turbulence[0] = static_cast<real_t>(0.0);
texopt->turbulence[1] = static_cast<real_t>(0.0);
texopt->turbulence[2] = static_cast<real_t>(0.0);
texopt->type = TEXTURE_TYPE_NONE;
const char *token = linebuf; // Assume line ends with NULL const char *token = linebuf; // Assume line ends with NULL
while (!IS_NEW_LINE((*token))) { while (!IS_NEW_LINE((*token))) {
@@ -998,7 +985,46 @@ static bool ParseTextureNameAndOption(std::string *texname,
} }
} }
static void InitTexOpt(texture_option_t *texopt, const bool is_bump) {
if (is_bump) {
texopt->imfchan = 'l';
} else {
texopt->imfchan = 'm';
}
texopt->bump_multiplier = static_cast<real_t>(1.0);
texopt->clamp = false;
texopt->blendu = true;
texopt->blendv = true;
texopt->sharpness = static_cast<real_t>(1.0);
texopt->brightness = static_cast<real_t>(0.0);
texopt->contrast = static_cast<real_t>(1.0);
texopt->origin_offset[0] = static_cast<real_t>(0.0);
texopt->origin_offset[1] = static_cast<real_t>(0.0);
texopt->origin_offset[2] = static_cast<real_t>(0.0);
texopt->scale[0] = static_cast<real_t>(1.0);
texopt->scale[1] = static_cast<real_t>(1.0);
texopt->scale[2] = static_cast<real_t>(1.0);
texopt->turbulence[0] = static_cast<real_t>(0.0);
texopt->turbulence[1] = static_cast<real_t>(0.0);
texopt->turbulence[2] = static_cast<real_t>(0.0);
texopt->type = TEXTURE_TYPE_NONE;
}
static void InitMaterial(material_t *material) { static void InitMaterial(material_t *material) {
InitTexOpt(&material->ambient_texopt, /* is_bump */ false);
InitTexOpt(&material->diffuse_texopt, /* is_bump */ false);
InitTexOpt(&material->specular_texopt, /* is_bump */ false);
InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false);
InitTexOpt(&material->bump_texopt, /* is_bump */ true);
InitTexOpt(&material->displacement_texopt, /* is_bump */ false);
InitTexOpt(&material->alpha_texopt, /* is_bump */ false);
InitTexOpt(&material->reflection_texopt, /* is_bump */ false);
InitTexOpt(&material->roughness_texopt, /* is_bump */ false);
InitTexOpt(&material->metallic_texopt, /* is_bump */ false);
InitTexOpt(&material->sheen_texopt, /* is_bump */ false);
InitTexOpt(&material->emissive_texopt, /* is_bump */ false);
InitTexOpt(&material->normal_texopt,
/* is_bump */ false); // @fixme { is_bump will be true? }
material->name = ""; material->name = "";
material->ambient_texname = ""; material->ambient_texname = "";
material->diffuse_texname = ""; material->diffuse_texname = "";
@@ -1145,20 +1171,34 @@ static bool exportGroupsToShape(shape_t *shape,
area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5); area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5);
} }
int maxRounds = 10; // arbitrary max loop count to protect against
// unexpected errors
face_t remainingFace = face; // copy face_t remainingFace = face; // copy
size_t guess_vert = 0; size_t guess_vert = 0;
vertex_index_t ind[3]; vertex_index_t ind[3];
real_t vx[3]; real_t vx[3];
real_t vy[3]; real_t vy[3];
while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) {
// How many iterations can we do without decreasing the remaining
// vertices.
size_t remainingIterations = face.vertex_indices.size();
size_t previousRemainingVertices = remainingFace.vertex_indices.size();
while (remainingFace.vertex_indices.size() > 3 && remainingIterations > 0){
npolys = remainingFace.vertex_indices.size(); npolys = remainingFace.vertex_indices.size();
if (guess_vert >= npolys) { if (guess_vert >= npolys) {
maxRounds -= 1;
guess_vert -= npolys; guess_vert -= npolys;
} }
if (previousRemainingVertices != npolys) {
// The number of remaining vertices decreased. Reset counters.
previousRemainingVertices = npolys;
remainingIterations = npolys;
} else {
// We didn't consume a vertex on previous iteration, reduce the
// available iterations.
remainingIterations--;
}
for (size_t k = 0; k < 3; k++) { for (size_t k = 0; k < 3; k++) {
ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
size_t vi = size_t(ind[k].v_idx); size_t vi = size_t(ind[k].v_idx);
@@ -1329,7 +1369,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
std::string linebuf; std::string linebuf;
while (inStream->peek() != -1) { while (inStream->peek() != -1) {
safeGetline(*inStream, linebuf); safeGetline(*inStream, linebuf);
line_no++; line_no++;
// Trim trailing whitespace. // Trim trailing whitespace.
if (linebuf.size() > 0) { if (linebuf.size() > 0) {
@@ -1469,9 +1509,9 @@ void LoadMtl(std::map<std::string, int> *material_map,
if (has_tr) { if (has_tr) {
warn_ss << "Both `d` and `Tr` parameters defined for \"" warn_ss << "Both `d` and `Tr` parameters defined for \""
<< material.name << "\". Use the value of `d` for dissolve (line " << material.name
<< line_no << " in .mtl.)" << "\". Use the value of `d` for dissolve (line " << line_no
<< std::endl; << " in .mtl.)" << std::endl;
} }
has_d = true; has_d = true;
continue; continue;
@@ -1481,9 +1521,9 @@ void LoadMtl(std::map<std::string, int> *material_map,
if (has_d) { if (has_d) {
// `d` wins. Ignore `Tr` value. // `d` wins. Ignore `Tr` value.
warn_ss << "Both `d` and `Tr` parameters defined for \"" warn_ss << "Both `d` and `Tr` parameters defined for \""
<< material.name << "\". Use the value of `d` for dissolve (line " << material.name
<< line_no << " in .mtl.)" << "\". Use the value of `d` for dissolve (line " << line_no
<< std::endl; << " in .mtl.)" << std::endl;
} else { } else {
// We invert value of Tr(assume Tr is in range [0, 1]) // We invert value of Tr(assume Tr is in range [0, 1])
// NOTE: Interpretation of Tr is application(exporter) dependent. For // NOTE: Interpretation of Tr is application(exporter) dependent. For
@@ -1547,8 +1587,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.ambient_texname), ParseTextureNameAndOption(&(material.ambient_texname),
&(material.ambient_texopt), token, &(material.ambient_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1556,8 +1595,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.diffuse_texname), ParseTextureNameAndOption(&(material.diffuse_texname),
&(material.diffuse_texopt), token, &(material.diffuse_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1565,8 +1603,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.specular_texname), ParseTextureNameAndOption(&(material.specular_texname),
&(material.specular_texopt), token, &(material.specular_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1574,8 +1611,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.specular_highlight_texname), ParseTextureNameAndOption(&(material.specular_highlight_texname),
&(material.specular_highlight_texopt), token, &(material.specular_highlight_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1583,8 +1619,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
token += 9; token += 9;
ParseTextureNameAndOption(&(material.bump_texname), ParseTextureNameAndOption(&(material.bump_texname),
&(material.bump_texopt), token, &(material.bump_texopt), token);
/* is_bump */ true);
continue; continue;
} }
@@ -1592,8 +1627,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) {
token += 9; token += 9;
ParseTextureNameAndOption(&(material.bump_texname), ParseTextureNameAndOption(&(material.bump_texname),
&(material.bump_texopt), token, &(material.bump_texopt), token);
/* is_bump */ true);
continue; continue;
} }
@@ -1601,8 +1635,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
ParseTextureNameAndOption(&(material.bump_texname), ParseTextureNameAndOption(&(material.bump_texname),
&(material.bump_texopt), token, &(material.bump_texopt), token);
/* is_bump */ true);
continue; continue;
} }
@@ -1611,8 +1644,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
token += 6; token += 6;
material.alpha_texname = token; material.alpha_texname = token;
ParseTextureNameAndOption(&(material.alpha_texname), ParseTextureNameAndOption(&(material.alpha_texname),
&(material.alpha_texopt), token, &(material.alpha_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1620,8 +1652,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
ParseTextureNameAndOption(&(material.displacement_texname), ParseTextureNameAndOption(&(material.displacement_texname),
&(material.displacement_texopt), token, &(material.displacement_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1629,8 +1660,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
ParseTextureNameAndOption(&(material.reflection_texname), ParseTextureNameAndOption(&(material.reflection_texname),
&(material.reflection_texopt), token, &(material.reflection_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1638,8 +1668,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.roughness_texname), ParseTextureNameAndOption(&(material.roughness_texname),
&(material.roughness_texopt), token, &(material.roughness_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1647,8 +1676,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.metallic_texname), ParseTextureNameAndOption(&(material.metallic_texname),
&(material.metallic_texopt), token, &(material.metallic_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1656,8 +1684,7 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.sheen_texname), ParseTextureNameAndOption(&(material.sheen_texname),
&(material.sheen_texopt), token, &(material.sheen_texopt), token);
/* is_bump */ false);
continue; continue;
} }
@@ -1665,17 +1692,15 @@ void LoadMtl(std::map<std::string, int> *material_map,
if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
token += 7; token += 7;
ParseTextureNameAndOption(&(material.emissive_texname), ParseTextureNameAndOption(&(material.emissive_texname),
&(material.emissive_texopt), token, &(material.emissive_texopt), token);
/* is_bump */ false);
continue; continue;
} }
// PBR: normal map texture // PBR: normal map texture
if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
token += 5; token += 5;
ParseTextureNameAndOption( ParseTextureNameAndOption(&(material.normal_texname),
&(material.normal_texname), &(material.normal_texopt), token, &(material.normal_texopt), token);
/* is_bump */ false); // @fixme { is_bump will be true? }
continue; continue;
} }
@@ -1934,7 +1959,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
static_cast<int>(vt.size() / 2), &vi)) { static_cast<int>(vt.size() / 2), &vi)) {
if (err) { if (err) {
std::stringstream ss; std::stringstream ss;
ss << "Failed parse `f' line(e.g. zero value for face index. line " << line_num << ".)\n"; ss << "Failed parse `f' line(e.g. zero value for face index. line "
<< line_num << ".)\n";
(*err) += ss.str(); (*err) += ss.str();
} }
return false; return false;
@@ -1996,7 +2022,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (warn) { if (warn) {
std::stringstream ss; std::stringstream ss;
ss << "Looks like empty filename for mtllib. Use default " ss << "Looks like empty filename for mtllib. Use default "
"material (line " << line_num << ".)\n"; "material (line "
<< line_num << ".)\n";
(*warn) += ss.str(); (*warn) += ss.str();
} }
@@ -2205,21 +2232,24 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (greatest_v_idx >= static_cast<int>(v.size() / 3)) { if (greatest_v_idx >= static_cast<int>(v.size() / 3)) {
if (warn) { if (warn) {
std::stringstream ss; std::stringstream ss;
ss << "Vertex indices out of bounds (line " << line_num << ".)\n" << std::endl; ss << "Vertex indices out of bounds (line " << line_num << ".)\n"
<< std::endl;
(*warn) += ss.str(); (*warn) += ss.str();
} }
} }
if (greatest_vn_idx >= static_cast<int>(vn.size() / 3)) { if (greatest_vn_idx >= static_cast<int>(vn.size() / 3)) {
if (warn) { if (warn) {
std::stringstream ss; std::stringstream ss;
ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n" << std::endl; ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n"
<< std::endl;
(*warn) += ss.str(); (*warn) += ss.str();
} }
} }
if (greatest_vt_idx >= static_cast<int>(vt.size() / 2)) { if (greatest_vt_idx >= static_cast<int>(vt.size() / 2)) {
if (warn) { if (warn) {
std::stringstream ss; std::stringstream ss;
ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n" << std::endl; ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n"
<< std::endl;
(*warn) += ss.str(); (*warn) += ss.str();
} }
} }