10 Commits

Author SHA1 Message Date
Syoyo Fujita
7b6e33da52 Rename vector type.
Refactor file layout of extperimental code.
2017-11-12 16:46:25 +09:00
Richard Fabian
5896933508 Added ear clipping triangulation. Added a tiny vertex type to the library to facilitate the change. 2017-11-11 22:37:52 +00:00
Syoyo Fujita
b434c2497f Update master URL. 2017-10-16 17:48:45 +09:00
Syoyo Fujita
94fc413466 Fix parsing of crease tags(t)
Support parsing texture filename containing whitespace.
2017-10-13 18:13:36 +09:00
Syoyo Fujita
1c6dbf9bd8 Merge branch 'master' of github.com:syoyo/tinyobjloader 2017-10-13 02:04:11 +09:00
Syoyo Fujita
5cd30b70e0 Merge pull request #142 from Ododo/master
[Python] Fix mapping for #131 and compilation error #139 #132 (related)
2017-10-08 15:09:07 +09:00
Ododo
eb1f395101 Fix mapping for #131 and compilation error #139 #132 (related) 2017-10-08 04:00:14 +02:00
Syoyo Fujita
27bdd547f0 If you use tinyobjloader, please let us know via github issue!. 2017-09-25 02:36:15 +09:00
Syoyo Fujita
75a4bd1d35 Add zero-value check when parsing `f' line. Fixes #140. 2017-09-25 02:30:24 +09:00
Syoyo Fujita
7c7335c907 Add test for parsing bump_multipler for normal map. 2017-09-15 16:34:31 +09:00
23 changed files with 448 additions and 142 deletions

View File

@@ -10,7 +10,7 @@
[![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master)
http://syoyo.github.io/tinyobjloader/
[https://github.com/syoyo/tinyobjloader](https://github.com/syoyo/tinyobjloader)
Tiny but powerful single file wavefront obj loader written in C++. No dependency except for C++ STL. It can parse over 10M polygons with moderate memory and time.
@@ -26,11 +26,20 @@ Old version is available `v0.9.x` branch https://github.com/syoyo/tinyobjloader/
## What's new
### Version 2.x
* Refactor API
* Support triangulation for concave polygons(#151)
### Version 1.x
Avaiable in `v1.x.y` branch.
* 20 Aug, 2016 : Bump version v1.0.0. New data structure and API!
### Old version
### Older version
Previous old version is avaiable in `v0.9.x` branch.
Older version is avaiable in `v0.9.x` branch.
## Example
@@ -49,7 +58,11 @@ http://casual-effects.com/data/index.html
TinyObjLoader is successfully used in ...
### New version(v1.0.x)
### New version(v2.x)
* Your project here! (Letting us know via github issue is welcome!)
### Old version(v1.x)
* Double precision support through `TINYOBJLOADER_USE_DOUBLE` thanks to noma
* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models
@@ -58,9 +71,9 @@ TinyObjLoader is successfully used in ...
* cudabox: CUDA Solid Voxelizer Engine https://github.com/gaspardzoss/cudavox
* Drake: A planning, control, and analysis toolbox for nonlinear dynamical systems https://github.com/RobotLocomotion/drake
* VFPR - a Vulkan Forward Plus Renderer : https://github.com/WindyDarian/Vulkan-Forward-Plus-Renderer
* Your project here!
* Your project here! (Letting us know via github issue is welcome!)
### Old version(v0.9.x)
### Older version(v0.9.x)
* bullet3 https://github.com/erwincoumans/bullet3
* pbrt-v2 https://github.com/mmp/pbrt-v2
@@ -228,12 +241,12 @@ for (size_t s = 0; s < shapes.size(); s++) {
for (size_t v = 0; v < fv; v++) {
// access to vertex
tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v];
tinyobj::real_t vx = attrib.vertices[3*idx.vertex_index+0];
tinyobj::real_t vy = attrib.vertices[3*idx.vertex_index+1];
tinyobj::real_t vz = attrib.vertices[3*idx.vertex_index+2];
tinyobj::real_t nx = attrib.normals[3*idx.normal_index+0];
tinyobj::real_t ny = attrib.normals[3*idx.normal_index+1];
tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2];
tinyobj::real_t vx = attrib.vertices[idx.vertex_index].x;
tinyobj::real_t vy = attrib.vertices[idx.vertex_index].y;
tinyobj::real_t vz = attrib.vertices[idx.vertex_index].z;
tinyobj::real_t nx = attrib.normals[idx.normal_index].x;
tinyobj::real_t ny = attrib.normals[idx.normal_index].y;
tinyobj::real_t nz = attrib.normals[idx.normal_index].z;
tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0];
tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1];
// Optional: vertex colors

View File

@@ -106,24 +106,24 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl;
for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
for (size_t v = 0; v < attrib.vertices.size(); v++) {
printf(" v[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.vertices[3 * v + 0]),
static_cast<const double>(attrib.vertices[3 * v + 1]),
static_cast<const double>(attrib.vertices[3 * v + 2]));
static_cast<const double>(attrib.vertices[v].x),
static_cast<const double>(attrib.vertices[v].y),
static_cast<const double>(attrib.vertices[v].z));
}
for (size_t v = 0; v < attrib.normals.size() / 3; v++) {
for (size_t v = 0; v < attrib.normals.size(); v++) {
printf(" n[%ld] = (%f, %f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.normals[3 * v + 0]),
static_cast<const double>(attrib.normals[3 * v + 1]),
static_cast<const double>(attrib.normals[3 * v + 2]));
static_cast<const double>(attrib.normals[v].x),
static_cast<const double>(attrib.normals[v].y),
static_cast<const double>(attrib.normals[v].z));
}
for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) {
for (size_t v = 0; v < attrib.texcoords.size(); v++) {
printf(" uv[%ld] = (%f, %f)\n", static_cast<long>(v),
static_cast<const double>(attrib.texcoords[2 * v + 0]),
static_cast<const double>(attrib.texcoords[2 * v + 1]));
static_cast<const double>(attrib.texcoords[v].x),
static_cast<const double>(attrib.texcoords[v].y));
}
// For each shape

View File

@@ -0,0 +1,2 @@
newmtl main
Kd 1 1 1

View File

@@ -0,0 +1,17 @@
mtllib issue-140-zero-face-idx.mtl
v -0.5 -0.5 0
v 0.5 -0.5 0
v 0.5 0.5 0
v -0.5 0.5 0
vt 0 0 0
vt 1 0 0
vt 1 1 0
vt 0 1 0
vn 0 0 -1
usemtl main
f 0/0/0 1/1/0 3/3/0
f 1/1/0 3/3/0 2/2/0

7
models/norm-texopt.mtl Normal file
View File

@@ -0,0 +1,7 @@
newmtl default
Ka 0 0 0
Kd 0 0 0
Ks 0 0 0
Kt 0.1 0.2 0.3
norm -bm 3 normalmap.jpg

7
models/norm-texopt.obj Normal file
View File

@@ -0,0 +1,7 @@
mtllib norm-texopt.mtl
o Test
v 1.864151 -1.219172 -5.532511
v 0.575869 -0.666304 5.896140
v 0.940448 1.000000 -1.971128
usemtl default
f 1 2 3

View File

@@ -0,0 +1,28 @@
newmtl white
Ka 0 0 0
Kd 1 1 1
Ks 0 0 0
# filename with white space.
map_Kd texture 01.png
newmtl red
Ka 0 0 0
Kd 1 0 0
Ks 0 0 0
# texture option + filename with white space.
bump -bm 2 bump 01.png
newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
newmtl light
Ka 20 20 20
Kd 1 1 1
Ks 0 0 0

View File

@@ -0,0 +1,31 @@
mtllib texture-filename-with-whitespace.mtl
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
# 8 vertices
g front cube
usemtl white
f 1 2 3 4
g back cube
# expects white material
f 8 7 6 5
g right cube
usemtl red
f 4 3 7 8
g top cube
usemtl white
f 5 1 4 8
g left cube
usemtl green
f 5 6 2 1
g bottom cube
usemtl white
f 2 6 7 3
# 6 elements

View File

@@ -48,7 +48,7 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
pyshapes = PyDict_New();
pymaterials = PyDict_New();
pymaterial_indices = PyDict_New();
pymaterial_indices = PyList_New(0);
rtndict = PyDict_New();
attribobj = PyDict_New();
@@ -124,53 +124,52 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
}
long material_index = 0;
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
mat != materials.end(); mat++) {
PyObject* matobj = PyDict_New();
PyObject* unknown_parameter = PyDict_New();
for (std::map<std::string, std::string>::iterator p =
(*mat).unknown_parameter.begin();
p != (*mat).unknown_parameter.end(); ++p) {
mat->unknown_parameter.begin();
p != mat->unknown_parameter.end(); ++p) {
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
PyUnicode_FromString(p->second.c_str()));
}
PyDict_SetItemString(matobj, "shininess",
PyFloat_FromDouble((*mat).shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble((*mat).ior));
PyFloat_FromDouble(mat->shininess));
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior));
PyDict_SetItemString(matobj, "dissolve",
PyFloat_FromDouble((*mat).dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong((*mat).illum));
PyFloat_FromDouble(mat->dissolve));
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum));
PyDict_SetItemString(matobj, "ambient_texname",
PyUnicode_FromString((*mat).ambient_texname.c_str()));
PyUnicode_FromString(mat->ambient_texname.c_str()));
PyDict_SetItemString(matobj, "diffuse_texname",
PyUnicode_FromString((*mat).diffuse_texname.c_str()));
PyUnicode_FromString(mat->diffuse_texname.c_str()));
PyDict_SetItemString(matobj, "specular_texname",
PyUnicode_FromString((*mat).specular_texname.c_str()));
PyUnicode_FromString(mat->specular_texname.c_str()));
PyDict_SetItemString(
matobj, "specular_highlight_texname",
PyUnicode_FromString((*mat).specular_highlight_texname.c_str()));
PyUnicode_FromString(mat->specular_highlight_texname.c_str()));
PyDict_SetItemString(matobj, "bump_texname",
PyUnicode_FromString((*mat).bump_texname.c_str()));
PyUnicode_FromString(mat->bump_texname.c_str()));
PyDict_SetItemString(
matobj, "displacement_texname",
PyUnicode_FromString((*mat).displacement_texname.c_str()));
PyUnicode_FromString(mat->displacement_texname.c_str()));
PyDict_SetItemString(matobj, "alpha_texname",
PyUnicode_FromString((*mat).alpha_texname.c_str()));
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3((*mat).ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3((*mat).diffuse));
PyUnicode_FromString(mat->alpha_texname.c_str()));
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
PyDict_SetItemString(matobj, "specular",
pyTupleFromfloat3((*mat).specular));
pyTupleFromfloat3(mat->specular));
PyDict_SetItemString(matobj, "transmittance",
pyTupleFromfloat3((*mat).transmittance));
pyTupleFromfloat3(mat->transmittance));
PyDict_SetItemString(matobj, "emission",
pyTupleFromfloat3((*mat).emission));
pyTupleFromfloat3(mat->emission));
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
PyDict_SetItemString(pymaterials, (*mat).name.c_str(), matobj);
PyDict_SetItemString(pymaterial_indices, PyLong_FromLong(material_index++), (*mat).name.c_str());
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str()));
}
PyDict_SetItemString(rtndict, "shapes", pyshapes);

View File

@@ -631,13 +631,14 @@ TEST_CASE("vertex-col-ext", "[Issue144]") {
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube-vertexcol.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
PrintInfo(attrib, shapes, materials);
//PrintInfo(attrib, shapes, materials);
REQUIRE(true == ret);
REQUIRE((8 * 3) == attrib.colors.size());
@@ -655,6 +656,64 @@ TEST_CASE("vertex-col-ext", "[Issue144]") {
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 0]));
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 1]));
REQUIRE(1 == Approx(attrib.colors[3 * 7 + 2]));
}
TEST_CASE("norm_texopts", "[norm]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/norm-texopt.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(1 == shapes.size());
REQUIRE(1 == materials.size());
REQUIRE(3.0 == Approx(materials[0].normal_texopt.bump_multiplier));
}
TEST_CASE("zero-face-idx-value", "[Issue140]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-140-zero-face-idx.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(false == ret);
REQUIRE(!err.empty());
}
TEST_CASE("texture-name-whitespace", "[Issue145]") {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-filename-with-whitespace.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << "[Issue145] " << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(err.empty());
REQUIRE(2 < materials.size());
REQUIRE(0 == materials[0].diffuse_texname.compare("texture 01.png"));
REQUIRE(0 == materials[1].bump_texname.compare("bump 01.png"));
REQUIRE(2 == Approx(materials[1].bump_texopt.bump_multiplier));
}

View File

@@ -23,6 +23,7 @@ THE SOFTWARE.
*/
//
// version 2.0.0 : New API! And support triangulation of concave polygon(#151).
// version 1.1.0 : Support parsing vertex color(#144)
// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
// version 1.0.7 : Support multiple tex options(#126)
@@ -101,13 +102,21 @@ namespace tinyobj {
// cube_left | cube_right
#ifdef TINYOBJLOADER_USE_DOUBLE
//#pragma message "using double"
typedef double real_t;
//#pragma message "using double"
typedef double real_t;
#else
//#pragma message "using float"
typedef float real_t;
//#pragma message "using float"
typedef float real_t;
#endif
struct vec3_t { real_t x,y,z; };
inline vec3_t operator+(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.x+r.x;o.y=l.y+r.y;o.z=l.z+r.z;return o;}
inline vec3_t operator-(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.x-r.x;o.y=l.y-r.y;o.z=l.z-r.z;return o;}
inline vec3_t operator*(const vec3_t &l,real_t r){vec3_t o;o.x=l.x*r;o.y=l.y*r;o.z=l.z*r;return o;}
inline vec3_t cross(const vec3_t &l,const vec3_t &r){vec3_t o;o.x=l.y*r.z-r.y*l.z;o.y=l.z*r.x-r.z*l.x;o.z=l.x*r.y-r.x*l.y;return o;}
inline real_t dot(const vec3_t &l,const vec3_t &r){return l.x*r.x+l.y*r.y+l.z*r.z;}
struct vec2_t { real_t x,y; };
typedef enum {
TEXTURE_TYPE_NONE, // default
TEXTURE_TYPE_SPHERE,
@@ -120,7 +129,7 @@ typedef enum {
} texture_type_t;
typedef struct {
texture_type_t type; // -type (default TEXTURE_TYPE_NONE)
texture_type_t type; // -type (default TEXTURE_TYPE_NONE)
real_t sharpness; // -boost (default 1.0?)
real_t brightness; // base_value in -mm option (default 0)
real_t contrast; // gain_value in -mm option (default 1)
@@ -228,9 +237,9 @@ typedef struct {
// Vertex attributes
typedef struct {
std::vector<real_t> vertices; // 'v'
std::vector<real_t> normals; // 'vn'
std::vector<real_t> texcoords; // 'vt'
std::vector<vec3_t> vertices; // 'v'
std::vector<vec3_t> normals; // 'vn'
std::vector<vec2_t> texcoords; // 'vt'
std::vector<real_t> colors; // extension: vertex colors
} attrib_t;
@@ -366,7 +375,6 @@ namespace tinyobj {
MaterialReader::~MaterialReader() {}
struct vertex_index {
int v_idx, vt_idx, vn_idx;
vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
@@ -430,10 +438,27 @@ static std::istream &safeGetline(std::istream &is, std::string &t) {
#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
// Make index zero-base, and also support relative index.
static inline int fixIndex(int idx, int n) {
if (idx > 0) return idx - 1;
if (idx == 0) return 0;
return n + idx; // negative value = relative
static inline bool fixIndex(int idx, int n, int *ret) {
if (!ret) {
return false;
}
if (idx > 0) {
(*ret) = idx - 1;
return true;
}
if (idx == 0) {
// zero is not allowed according to the spec.
return false;
}
if (idx < 0) {
(*ret) = n + idx; // negative value = relative
return true;
}
return false; // never reach here.
}
static inline std::string parseString(const char **token) {
@@ -586,9 +611,9 @@ static bool tryParseDouble(const char *s, const char *s_end, double *result) {
}
assemble:
*result =
(sign == '+' ? 1 : -1) *
(exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa);
*result = (sign == '+' ? 1 : -1) *
(exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent)
: mantissa);
return true;
fail:
return false;
@@ -618,16 +643,16 @@ static inline bool parseReal(const char **token, real_t *out) {
}
static inline void parseReal2(real_t *x, real_t *y, const char **token,
const double default_x = 0.0,
const double default_y = 0.0) {
const double default_x = 0.0,
const double default_y = 0.0) {
(*x) = parseReal(token, default_x);
(*y) = parseReal(token, default_y);
}
static inline void parseReal3(real_t *x, real_t *y, real_t *z, const char **token,
const double default_x = 0.0,
const double default_y = 0.0,
const double default_z = 0.0) {
static inline void parseReal3(real_t *x, real_t *y, real_t *z,
const char **token, const double default_x = 0.0,
const double default_y = 0.0,
const double default_z = 0.0) {
(*x) = parseReal(token, default_x);
(*y) = parseReal(token, default_y);
(*z) = parseReal(token, default_z);
@@ -705,58 +730,80 @@ static inline texture_type_t parseTextureType(
static tag_sizes parseTagTriple(const char **token) {
tag_sizes ts;
(*token) += strspn((*token), " \t");
ts.num_ints = atoi((*token));
(*token) += strcspn((*token), "/ \t\r");
if ((*token)[0] != '/') {
return ts;
}
(*token)++;
(*token)++; // Skip '/'
(*token) += strspn((*token), " \t");
ts.num_reals = atoi((*token));
(*token) += strcspn((*token), "/ \t\r");
if ((*token)[0] != '/') {
return ts;
}
(*token)++;
(*token)++; // Skip '/'
ts.num_strings = atoi((*token));
(*token) += strcspn((*token), "/ \t\r") + 1;
ts.num_strings = parseInt(token);
return ts;
}
// Parse triples with index offsets: i, i/j/k, i//k, i/j
static vertex_index parseTriple(const char **token, int vsize, int vnsize,
int vtsize) {
static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize,
vertex_index *ret) {
if (!ret) {
return false;
}
vertex_index vi(-1);
vi.v_idx = fixIndex(atoi((*token)), vsize);
if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) {
return false;
}
(*token) += strcspn((*token), "/ \t\r");
if ((*token)[0] != '/') {
return vi;
(*ret) = vi;
return true;
}
(*token)++;
// i//k
if ((*token)[0] == '/') {
(*token)++;
vi.vn_idx = fixIndex(atoi((*token)), vnsize);
if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) {
return false;
}
(*token) += strcspn((*token), "/ \t\r");
return vi;
(*ret) = vi;
return true;
}
// i/j/k or i/j
vi.vt_idx = fixIndex(atoi((*token)), vtsize);
if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) {
return false;
}
(*token) += strcspn((*token), "/ \t\r");
if ((*token)[0] != '/') {
return vi;
(*ret) = vi;
return true;
}
// i/j/k
(*token)++; // skip '/'
vi.vn_idx = fixIndex(atoi((*token)), vnsize);
if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) {
return false;
}
(*token) += strcspn((*token), "/ \t\r");
return vi;
(*ret) = vi;
return true;
}
// Parse raw triples: i, i/j/k, i//k, i/j
@@ -845,15 +892,15 @@ static bool ParseTextureNameAndOption(std::string *texname,
} else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) {
token += 3;
parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]),
&(texopt->origin_offset[2]), &token);
&(texopt->origin_offset[2]), &token);
} else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) {
token += 3;
parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]),
&token, 1.0, 1.0, 1.0);
&token, 1.0, 1.0, 1.0);
} else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) {
token += 3;
parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]),
&(texopt->turbulence[2]), &token);
&(texopt->turbulence[2]), &token);
} else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) {
token += 5;
texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE);
@@ -870,11 +917,18 @@ static bool ParseTextureNameAndOption(std::string *texname,
parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
} else {
// Assume texture filename
#if 0
size_t len = strcspn(token, " \t\r"); // untile next space
texture_name = std::string(token, token + len);
token += len;
token += strspn(token, " \t"); // skip space
#else
// Read filename until line end to parse filename containing whitespace
// TODO(syoyo): Support parsing texture option flag after the filename.
texture_name = std::string(token);
token += texture_name.length();
#endif
found_texname = true;
}
@@ -929,7 +983,7 @@ static void InitMaterial(material_t *material) {
static bool exportFaceGroupToShape(
shape_t *shape, const std::vector<std::vector<vertex_index> > &faceGroup,
const std::vector<tag_t> &tags, const int material_id,
const std::string &name, bool triangulate) {
const std::string &name, bool triangulate, const std::vector<vec3_t> &v ) {
if (faceGroup.empty()) {
return false;
}
@@ -945,29 +999,124 @@ static bool exportFaceGroupToShape(
size_t npolys = face.size();
if (triangulate) {
// Polygon -> triangle fan conversion
for (size_t k = 2; k < npolys; k++) {
i1 = i2;
i2 = face[k];
vec3_t face_normal = {0,0,0};
index_t idx0, idx1, idx2;
idx0.vertex_index = i0.v_idx;
idx0.normal_index = i0.vn_idx;
idx0.texcoord_index = i0.vt_idx;
idx1.vertex_index = i1.v_idx;
idx1.normal_index = i1.vn_idx;
idx1.texcoord_index = i1.vt_idx;
idx2.vertex_index = i2.v_idx;
idx2.normal_index = i2.vn_idx;
idx2.texcoord_index = i2.vt_idx;
for(size_t k = 0; k < npolys; ++k) {
int vi0 = face[(k+0)%npolys].v_idx;
int vi1 = face[(k+1)%npolys].v_idx;
int vi2 = face[(k+2)%npolys].v_idx;
const vec3_t &v0 = v[vi0];
const vec3_t &v1 = v[vi1];
const vec3_t &v2 = v[vi2];
const vec3_t e0 = v1 - v0;
const vec3_t e1 = v2 - v1;
const vec3_t n = cross( e0, e1 );
face_normal = face_normal + n;
}
shape->mesh.indices.push_back(idx0);
shape->mesh.indices.push_back(idx1);
shape->mesh.indices.push_back(idx2);
// face_normal is currently area of face
{
real_t l = sqrt(dot( face_normal, face_normal ));
face_normal = face_normal * (1.0/l);
}
shape->mesh.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id);
}
int maxRounds = 10; // arbitrary max loop count to protect against unexpected errors
std::vector<vertex_index> remainingFace = face;
size_t guess_vert = 0;
while( remainingFace.size() > 3 && maxRounds > 0 ) {
npolys = remainingFace.size();
if( guess_vert >= npolys ) {
maxRounds -= 1;
guess_vert -= npolys;
}
i0 = remainingFace[(guess_vert+0)%npolys];
i1 = remainingFace[(guess_vert+1)%npolys];
i2 = remainingFace[(guess_vert+2)%npolys];
int vi0 = i0.v_idx;
int vi1 = i1.v_idx;
int vi2 = i2.v_idx;
const vec3_t &v0 = v[vi0];
const vec3_t &v1 = v[vi1];
const vec3_t &v2 = v[vi2];
const vec3_t e0 = v1 - v0;
const vec3_t e1 = v2 - v1;
const vec3_t n = cross( e0, e1 );
bool internal = dot( n, face_normal ) < 0.0f;
if( internal ) { guess_vert += 1; continue; }
// check all other verts in case they are inside this triangle
bool overlap = false;
for( size_t otherVert = 3; otherVert < npolys; ++otherVert ) {
int ovi = remainingFace[(guess_vert+otherVert)%npolys].v_idx;
const vec3_t &ov = v[ovi];
if( dot( face_normal, cross( e0, ov-v0 ) ) < 0 )
continue;
if( dot( face_normal, cross( e1, ov-v1 ) ) < 0 )
continue;
if( dot( face_normal, cross( v0-v2, ov-v2 ) ) < 0 )
continue;
// vert inside triangle
overlap = true;
break;
}
if( overlap ) { guess_vert += 1; continue; }
// this triangle is an ear
{
index_t idx0, idx1, idx2;
idx0.vertex_index = i0.v_idx;
idx0.normal_index = i0.vn_idx;
idx0.texcoord_index = i0.vt_idx;
idx1.vertex_index = i1.v_idx;
idx1.normal_index = i1.vn_idx;
idx1.texcoord_index = i1.vt_idx;
idx2.vertex_index = i2.v_idx;
idx2.normal_index = i2.vn_idx;
idx2.texcoord_index = i2.vt_idx;
shape->mesh.indices.push_back(idx0);
shape->mesh.indices.push_back(idx1);
shape->mesh.indices.push_back(idx2);
shape->mesh.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id);
}
// remove v1 from the list
size_t removed_vert_index = (guess_vert+1)%npolys;
while( removed_vert_index + 1 < npolys ) {
remainingFace[removed_vert_index] = remainingFace[removed_vert_index+1];
removed_vert_index += 1;
}
remainingFace.pop_back();
}
if( remainingFace.size() == 3 ) {
i0 = remainingFace[0];
i1 = remainingFace[1];
i2 = remainingFace[2];
{
index_t idx0, idx1, idx2;
idx0.vertex_index = i0.v_idx;
idx0.normal_index = i0.vn_idx;
idx0.texcoord_index = i0.vt_idx;
idx1.vertex_index = i1.v_idx;
idx1.normal_index = i1.vn_idx;
idx1.texcoord_index = i1.vt_idx;
idx2.vertex_index = i2.v_idx;
idx2.normal_index = i2.vn_idx;
idx2.texcoord_index = i2.vt_idx;
shape->mesh.indices.push_back(idx0);
shape->mesh.indices.push_back(idx1);
shape->mesh.indices.push_back(idx2);
shape->mesh.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id);
}
}
} else {
for (size_t k = 0; k < npolys; k++) {
index_t idx;
@@ -1483,9 +1632,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
bool triangulate) {
std::stringstream errss;
std::vector<real_t> v;
std::vector<real_t> vn;
std::vector<real_t> vt;
std::vector<vec3_t> v;
std::vector<vec3_t> vn;
std::vector<vec2_t> vt;
std::vector<real_t> vc;
std::vector<tag_t> tags;
std::vector<std::vector<vertex_index> > faceGroup;
@@ -1528,12 +1677,10 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// vertex
if (token[0] == 'v' && IS_SPACE((token[1]))) {
token += 2;
real_t x, y, z;
vec3_t v3;
real_t r, g, b;
parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token);
v.push_back(x);
v.push_back(y);
v.push_back(z);
parseVertexWithColor(&v3.x, &v3.y, &v3.z, &r, &g, &b, &token);
v.push_back(v3);
vc.push_back(r);
vc.push_back(g);
@@ -1544,21 +1691,18 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// normal
if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
token += 3;
real_t x, y, z;
parseReal3(&x, &y, &z, &token);
vn.push_back(x);
vn.push_back(y);
vn.push_back(z);
vec3_t v3;
parseReal3(&v3.x, &v3.y, &v3.z, &token);
vn.push_back(v3);
continue;
}
// texcoord
if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
token += 3;
real_t x, y;
parseReal2(&x, &y, &token);
vt.push_back(x);
vt.push_back(y);
vec2_t v2;
parseReal2(&v2.x, &v2.y, &token);
vt.push_back(v2);
continue;
}
@@ -1571,9 +1715,16 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
face.reserve(3);
while (!IS_NEW_LINE(token[0])) {
vertex_index vi = parseTriple(&token, static_cast<int>(v.size() / 3),
static_cast<int>(vn.size() / 3),
static_cast<int>(vt.size() / 2));
vertex_index vi;
if (!parseTriple(&token, static_cast<int>(v.size()),
static_cast<int>(vn.size()),
static_cast<int>(vt.size()), &vi)) {
if (err) {
(*err) = "Failed parse `f' line(e.g. zero value for face index).\n";
}
return false;
}
face.push_back(vi);
size_t n = strspn(token, " \t\r");
token += n;
@@ -1605,7 +1756,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// this time.
// just clear `faceGroup` after `exportFaceGroupToShape()` call.
exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate);
triangulate, v);
faceGroup.clear();
material = newMaterialId;
}
@@ -1660,8 +1811,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'g' && IS_SPACE((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate);
(void)ret; // return value not used.
triangulate, v);
(void)ret; // return value not used.
if (shape.mesh.indices.size() > 0) {
shapes->push_back(shape);
@@ -1697,7 +1848,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
if (token[0] == 'o' && IS_SPACE((token[1]))) {
// flush previous face group.
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate);
triangulate, v);
if (ret) {
shapes->push_back(shape);
}
@@ -1719,33 +1870,25 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
tag_t tag;
token += 2;
std::stringstream ss;
ss << token;
tag.name = ss.str();
token += tag.name.size() + 1;
tag.name = parseString(&token);
tag_sizes ts = parseTagTriple(&token);
tag.intValues.resize(static_cast<size_t>(ts.num_ints));
for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
tag.intValues[i] = atoi(token);
token += strcspn(token, "/ \t\r") + 1;
tag.intValues[i] = parseInt(&token);
}
tag.floatValues.resize(static_cast<size_t>(ts.num_reals));
for (size_t i = 0; i < static_cast<size_t>(ts.num_reals); ++i) {
tag.floatValues[i] = parseReal(&token);
token += strcspn(token, "/ \t\r") + 1;
}
tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
std::stringstream sstr;
sstr << token;
tag.stringValues[i] = sstr.str();
token += tag.stringValues[i].size() + 1;
tag.stringValues[i] = parseString(&token);
}
tags.push_back(tag);
@@ -1755,7 +1898,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
}
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
triangulate);
triangulate, v);
// exportFaceGroupToShape return false when `usemtl` is called in the last
// line.
// we also add `shape` to `shapes` when `shape.mesh` has already some