|
|
|
|
@@ -23,6 +23,8 @@ THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// version 1.2.2 : Parse multiple group names.
|
|
|
|
|
// version 1.2.1 : Added initial support for line('l') primitive(PR #178)
|
|
|
|
|
// version 1.2.0 : Hardened implementation(#175)
|
|
|
|
|
// version 1.1.1 : Support smoothing groups(#162)
|
|
|
|
|
// version 1.1.0 : Support parsing vertex color(#144)
|
|
|
|
|
@@ -236,9 +238,14 @@ typedef struct {
|
|
|
|
|
std::vector<tag_t> tags; // SubD tag
|
|
|
|
|
} mesh_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
std::vector<int> indices; // pairs of indices for lines
|
|
|
|
|
} path_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
std::string name;
|
|
|
|
|
mesh_t mesh;
|
|
|
|
|
path_t path;
|
|
|
|
|
} shape_t;
|
|
|
|
|
|
|
|
|
|
// Vertex attributes
|
|
|
|
|
@@ -372,8 +379,8 @@ void LoadMtl(std::map<std::string, int> *material_map,
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <utility>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
@@ -401,6 +408,11 @@ struct face_t {
|
|
|
|
|
face_t() : smoothing_group_id(0) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct line_t {
|
|
|
|
|
int idx0;
|
|
|
|
|
int idx1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct tag_sizes {
|
|
|
|
|
tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
|
|
|
|
|
int num_ints;
|
|
|
|
|
@@ -935,7 +947,7 @@ static bool ParseTextureNameAndOption(std::string *texname,
|
|
|
|
|
token += 4;
|
|
|
|
|
parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0);
|
|
|
|
|
} else {
|
|
|
|
|
// Assume texture filename
|
|
|
|
|
// Assume texture filename
|
|
|
|
|
#if 0
|
|
|
|
|
size_t len = strcspn(token, " \t\r"); // untile next space
|
|
|
|
|
texture_name = std::string(token, token + len);
|
|
|
|
|
@@ -1000,9 +1012,8 @@ static void InitMaterial(material_t *material) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
|
|
|
|
template<typename T>
|
|
|
|
|
static int pnpoly(int nvert, T *vertx, T *verty, T testx,
|
|
|
|
|
T testy) {
|
|
|
|
|
template <typename T>
|
|
|
|
|
static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) {
|
|
|
|
|
int i, j, c = 0;
|
|
|
|
|
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
|
|
|
|
|
if (((verty[i] > testy) != (verty[j] > testy)) &&
|
|
|
|
|
@@ -1015,245 +1026,249 @@ static int pnpoly(int nvert, T *vertx, T *verty, T testx,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(syoyo): refactor function.
|
|
|
|
|
static bool exportFaceGroupToShape(shape_t *shape,
|
|
|
|
|
const std::vector<face_t> &faceGroup,
|
|
|
|
|
const std::vector<tag_t> &tags,
|
|
|
|
|
const int material_id,
|
|
|
|
|
const std::string &name, bool triangulate,
|
|
|
|
|
const std::vector<real_t> &v) {
|
|
|
|
|
if (faceGroup.empty()) {
|
|
|
|
|
static bool exportGroupsToShape(shape_t *shape,
|
|
|
|
|
const std::vector<face_t> &faceGroup,
|
|
|
|
|
std::vector<int> &lineGroup,
|
|
|
|
|
const std::vector<tag_t> &tags,
|
|
|
|
|
const int material_id, const std::string &name,
|
|
|
|
|
bool triangulate,
|
|
|
|
|
const std::vector<real_t> &v) {
|
|
|
|
|
if (faceGroup.empty() && lineGroup.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Flatten vertices and indices
|
|
|
|
|
for (size_t i = 0; i < faceGroup.size(); i++) {
|
|
|
|
|
const face_t &face = faceGroup[i];
|
|
|
|
|
if (!faceGroup.empty()) {
|
|
|
|
|
// Flatten vertices and indices
|
|
|
|
|
for (size_t i = 0; i < faceGroup.size(); i++) {
|
|
|
|
|
const face_t &face = faceGroup[i];
|
|
|
|
|
|
|
|
|
|
size_t npolys = face.vertex_indices.size();
|
|
|
|
|
size_t npolys = face.vertex_indices.size();
|
|
|
|
|
|
|
|
|
|
if (npolys < 3) {
|
|
|
|
|
// Face must have 3+ vertices.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vertex_index_t i0 = face.vertex_indices[0];
|
|
|
|
|
vertex_index_t i1(-1);
|
|
|
|
|
vertex_index_t i2 = face.vertex_indices[1];
|
|
|
|
|
|
|
|
|
|
if (triangulate) {
|
|
|
|
|
// find the two axes to work in
|
|
|
|
|
size_t axes[2] = {1, 2};
|
|
|
|
|
for (size_t k = 0; k < npolys; ++k) {
|
|
|
|
|
i0 = face.vertex_indices[(k + 0) % npolys];
|
|
|
|
|
i1 = face.vertex_indices[(k + 1) % npolys];
|
|
|
|
|
i2 = face.vertex_indices[(k + 2) % npolys];
|
|
|
|
|
size_t vi0 = size_t(i0.v_idx);
|
|
|
|
|
size_t vi1 = size_t(i1.v_idx);
|
|
|
|
|
size_t vi2 = size_t(i2.v_idx);
|
|
|
|
|
|
|
|
|
|
if (((3 * vi0 + 2) >= v.size()) ||
|
|
|
|
|
((3 * vi1 + 2) >= v.size()) ||
|
|
|
|
|
((3 * vi2 + 2) >= v.size())) {
|
|
|
|
|
// Invalid triangle.
|
|
|
|
|
// FIXME(syoyo): Is it ok to simply skip this invalid triangle?
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
real_t v0x = v[vi0 * 3 + 0];
|
|
|
|
|
real_t v0y = v[vi0 * 3 + 1];
|
|
|
|
|
real_t v0z = v[vi0 * 3 + 2];
|
|
|
|
|
real_t v1x = v[vi1 * 3 + 0];
|
|
|
|
|
real_t v1y = v[vi1 * 3 + 1];
|
|
|
|
|
real_t v1z = v[vi1 * 3 + 2];
|
|
|
|
|
real_t v2x = v[vi2 * 3 + 0];
|
|
|
|
|
real_t v2y = v[vi2 * 3 + 1];
|
|
|
|
|
real_t v2z = v[vi2 * 3 + 2];
|
|
|
|
|
real_t e0x = v1x - v0x;
|
|
|
|
|
real_t e0y = v1y - v0y;
|
|
|
|
|
real_t e0z = v1z - v0z;
|
|
|
|
|
real_t e1x = v2x - v1x;
|
|
|
|
|
real_t e1y = v2y - v1y;
|
|
|
|
|
real_t e1z = v2z - v1z;
|
|
|
|
|
real_t cx = std::fabs(e0y * e1z - e0z * e1y);
|
|
|
|
|
real_t cy = std::fabs(e0z * e1x - e0x * e1z);
|
|
|
|
|
real_t cz = std::fabs(e0x * e1y - e0y * e1x);
|
|
|
|
|
const real_t epsilon = std::numeric_limits<real_t>::epsilon();
|
|
|
|
|
if (cx > epsilon || cy > epsilon || cz > epsilon) {
|
|
|
|
|
// found a corner
|
|
|
|
|
if (cx > cy && cx > cz) {
|
|
|
|
|
} else {
|
|
|
|
|
axes[0] = 0;
|
|
|
|
|
if (cz > cx && cz > cy) axes[1] = 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (npolys < 3) {
|
|
|
|
|
// Face must have 3+ vertices.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
real_t area = 0;
|
|
|
|
|
for (size_t k = 0; k < npolys; ++k) {
|
|
|
|
|
i0 = face.vertex_indices[(k + 0) % npolys];
|
|
|
|
|
i1 = face.vertex_indices[(k + 1) % npolys];
|
|
|
|
|
size_t vi0 = size_t(i0.v_idx);
|
|
|
|
|
size_t vi1 = size_t(i1.v_idx);
|
|
|
|
|
if (((vi0 * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi0 * 3 + axes[1]) >= v.size()) ||
|
|
|
|
|
((vi1 * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi1 * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// Invalid index.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
real_t v0x = v[vi0 * 3 + axes[0]];
|
|
|
|
|
real_t v0y = v[vi0 * 3 + axes[1]];
|
|
|
|
|
real_t v1x = v[vi1 * 3 + axes[0]];
|
|
|
|
|
real_t v1y = v[vi1 * 3 + axes[1]];
|
|
|
|
|
area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5);
|
|
|
|
|
}
|
|
|
|
|
vertex_index_t i0 = face.vertex_indices[0];
|
|
|
|
|
vertex_index_t i1(-1);
|
|
|
|
|
vertex_index_t i2 = face.vertex_indices[1];
|
|
|
|
|
|
|
|
|
|
int maxRounds =
|
|
|
|
|
10; // arbitrary max loop count to protect against unexpected errors
|
|
|
|
|
if (triangulate) {
|
|
|
|
|
// find the two axes to work in
|
|
|
|
|
size_t axes[2] = {1, 2};
|
|
|
|
|
for (size_t k = 0; k < npolys; ++k) {
|
|
|
|
|
i0 = face.vertex_indices[(k + 0) % npolys];
|
|
|
|
|
i1 = face.vertex_indices[(k + 1) % npolys];
|
|
|
|
|
i2 = face.vertex_indices[(k + 2) % npolys];
|
|
|
|
|
size_t vi0 = size_t(i0.v_idx);
|
|
|
|
|
size_t vi1 = size_t(i1.v_idx);
|
|
|
|
|
size_t vi2 = size_t(i2.v_idx);
|
|
|
|
|
|
|
|
|
|
face_t remainingFace = face; // copy
|
|
|
|
|
size_t guess_vert = 0;
|
|
|
|
|
vertex_index_t ind[3];
|
|
|
|
|
real_t vx[3];
|
|
|
|
|
real_t vy[3];
|
|
|
|
|
while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) {
|
|
|
|
|
npolys = remainingFace.vertex_indices.size();
|
|
|
|
|
if (guess_vert >= npolys) {
|
|
|
|
|
maxRounds -= 1;
|
|
|
|
|
guess_vert -= npolys;
|
|
|
|
|
}
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
|
|
|
|
|
size_t vi = size_t(ind[k].v_idx);
|
|
|
|
|
if (((vi * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// ???
|
|
|
|
|
vx[k] = static_cast<real_t>(0.0);
|
|
|
|
|
vy[k] = static_cast<real_t>(0.0);
|
|
|
|
|
} else {
|
|
|
|
|
vx[k] = v[vi * 3 + axes[0]];
|
|
|
|
|
vy[k] = v[vi * 3 + axes[1]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
real_t e0x = vx[1] - vx[0];
|
|
|
|
|
real_t e0y = vy[1] - vy[0];
|
|
|
|
|
real_t e1x = vx[2] - vx[1];
|
|
|
|
|
real_t e1y = vy[2] - vy[1];
|
|
|
|
|
real_t cross = e0x * e1y - e0y * e1x;
|
|
|
|
|
// if an internal angle
|
|
|
|
|
if (cross * area < static_cast<real_t>(0.0)) {
|
|
|
|
|
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) {
|
|
|
|
|
size_t idx = (guess_vert + otherVert) % npolys;
|
|
|
|
|
|
|
|
|
|
if (idx >= remainingFace.vertex_indices.size()) {
|
|
|
|
|
// ???
|
|
|
|
|
if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) ||
|
|
|
|
|
((3 * vi2 + 2) >= v.size())) {
|
|
|
|
|
// Invalid triangle.
|
|
|
|
|
// FIXME(syoyo): Is it ok to simply skip this invalid triangle?
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t ovi = size_t(
|
|
|
|
|
remainingFace.vertex_indices[idx]
|
|
|
|
|
.v_idx);
|
|
|
|
|
|
|
|
|
|
if (((ovi * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((ovi * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// ???
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
real_t tx = v[ovi * 3 + axes[0]];
|
|
|
|
|
real_t ty = v[ovi * 3 + axes[1]];
|
|
|
|
|
if (pnpoly(3, vx, vy, tx, ty)) {
|
|
|
|
|
overlap = true;
|
|
|
|
|
real_t v0x = v[vi0 * 3 + 0];
|
|
|
|
|
real_t v0y = v[vi0 * 3 + 1];
|
|
|
|
|
real_t v0z = v[vi0 * 3 + 2];
|
|
|
|
|
real_t v1x = v[vi1 * 3 + 0];
|
|
|
|
|
real_t v1y = v[vi1 * 3 + 1];
|
|
|
|
|
real_t v1z = v[vi1 * 3 + 2];
|
|
|
|
|
real_t v2x = v[vi2 * 3 + 0];
|
|
|
|
|
real_t v2y = v[vi2 * 3 + 1];
|
|
|
|
|
real_t v2z = v[vi2 * 3 + 2];
|
|
|
|
|
real_t e0x = v1x - v0x;
|
|
|
|
|
real_t e0y = v1y - v0y;
|
|
|
|
|
real_t e0z = v1z - v0z;
|
|
|
|
|
real_t e1x = v2x - v1x;
|
|
|
|
|
real_t e1y = v2y - v1y;
|
|
|
|
|
real_t e1z = v2z - v1z;
|
|
|
|
|
real_t cx = std::fabs(e0y * e1z - e0z * e1y);
|
|
|
|
|
real_t cy = std::fabs(e0z * e1x - e0x * e1z);
|
|
|
|
|
real_t cz = std::fabs(e0x * e1y - e0y * e1x);
|
|
|
|
|
const real_t epsilon = std::numeric_limits<real_t>::epsilon();
|
|
|
|
|
if (cx > epsilon || cy > epsilon || cz > epsilon) {
|
|
|
|
|
// found a corner
|
|
|
|
|
if (cx > cy && cx > cz) {
|
|
|
|
|
} else {
|
|
|
|
|
axes[0] = 0;
|
|
|
|
|
if (cz > cx && cz > cy) axes[1] = 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (overlap) {
|
|
|
|
|
guess_vert += 1;
|
|
|
|
|
continue;
|
|
|
|
|
real_t area = 0;
|
|
|
|
|
for (size_t k = 0; k < npolys; ++k) {
|
|
|
|
|
i0 = face.vertex_indices[(k + 0) % npolys];
|
|
|
|
|
i1 = face.vertex_indices[(k + 1) % npolys];
|
|
|
|
|
size_t vi0 = size_t(i0.v_idx);
|
|
|
|
|
size_t vi1 = size_t(i1.v_idx);
|
|
|
|
|
if (((vi0 * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi0 * 3 + axes[1]) >= v.size()) ||
|
|
|
|
|
((vi1 * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi1 * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// Invalid index.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
real_t v0x = v[vi0 * 3 + axes[0]];
|
|
|
|
|
real_t v0y = v[vi0 * 3 + axes[1]];
|
|
|
|
|
real_t v1x = v[vi1 * 3 + axes[0]];
|
|
|
|
|
real_t v1y = v[vi1 * 3 + axes[1]];
|
|
|
|
|
area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this triangle is an ear
|
|
|
|
|
{
|
|
|
|
|
index_t idx0, idx1, idx2;
|
|
|
|
|
idx0.vertex_index = ind[0].v_idx;
|
|
|
|
|
idx0.normal_index = ind[0].vn_idx;
|
|
|
|
|
idx0.texcoord_index = ind[0].vt_idx;
|
|
|
|
|
idx1.vertex_index = ind[1].v_idx;
|
|
|
|
|
idx1.normal_index = ind[1].vn_idx;
|
|
|
|
|
idx1.texcoord_index = ind[1].vt_idx;
|
|
|
|
|
idx2.vertex_index = ind[2].v_idx;
|
|
|
|
|
idx2.normal_index = ind[2].vn_idx;
|
|
|
|
|
idx2.texcoord_index = ind[2].vt_idx;
|
|
|
|
|
int maxRounds = 10; // arbitrary max loop count to protect against
|
|
|
|
|
// unexpected errors
|
|
|
|
|
|
|
|
|
|
shape->mesh.indices.push_back(idx0);
|
|
|
|
|
shape->mesh.indices.push_back(idx1);
|
|
|
|
|
shape->mesh.indices.push_back(idx2);
|
|
|
|
|
face_t remainingFace = face; // copy
|
|
|
|
|
size_t guess_vert = 0;
|
|
|
|
|
vertex_index_t ind[3];
|
|
|
|
|
real_t vx[3];
|
|
|
|
|
real_t vy[3];
|
|
|
|
|
while (remainingFace.vertex_indices.size() > 3 && maxRounds > 0) {
|
|
|
|
|
npolys = remainingFace.vertex_indices.size();
|
|
|
|
|
if (guess_vert >= npolys) {
|
|
|
|
|
maxRounds -= 1;
|
|
|
|
|
guess_vert -= npolys;
|
|
|
|
|
}
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys];
|
|
|
|
|
size_t vi = size_t(ind[k].v_idx);
|
|
|
|
|
if (((vi * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((vi * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// ???
|
|
|
|
|
vx[k] = static_cast<real_t>(0.0);
|
|
|
|
|
vy[k] = static_cast<real_t>(0.0);
|
|
|
|
|
} else {
|
|
|
|
|
vx[k] = v[vi * 3 + axes[0]];
|
|
|
|
|
vy[k] = v[vi * 3 + axes[1]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
real_t e0x = vx[1] - vx[0];
|
|
|
|
|
real_t e0y = vy[1] - vy[0];
|
|
|
|
|
real_t e1x = vx[2] - vx[1];
|
|
|
|
|
real_t e1y = vy[2] - vy[1];
|
|
|
|
|
real_t cross = e0x * e1y - e0y * e1x;
|
|
|
|
|
// if an internal angle
|
|
|
|
|
if (cross * area < static_cast<real_t>(0.0)) {
|
|
|
|
|
guess_vert += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape->mesh.num_face_vertices.push_back(3);
|
|
|
|
|
shape->mesh.material_ids.push_back(material_id);
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
|
|
|
|
|
// check all other verts in case they are inside this triangle
|
|
|
|
|
bool overlap = false;
|
|
|
|
|
for (size_t otherVert = 3; otherVert < npolys; ++otherVert) {
|
|
|
|
|
size_t idx = (guess_vert + otherVert) % npolys;
|
|
|
|
|
|
|
|
|
|
if (idx >= remainingFace.vertex_indices.size()) {
|
|
|
|
|
// ???
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx);
|
|
|
|
|
|
|
|
|
|
if (((ovi * 3 + axes[0]) >= v.size()) ||
|
|
|
|
|
((ovi * 3 + axes[1]) >= v.size())) {
|
|
|
|
|
// ???
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
real_t tx = v[ovi * 3 + axes[0]];
|
|
|
|
|
real_t ty = v[ovi * 3 + axes[1]];
|
|
|
|
|
if (pnpoly(3, vx, vy, tx, ty)) {
|
|
|
|
|
overlap = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (overlap) {
|
|
|
|
|
guess_vert += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this triangle is an ear
|
|
|
|
|
{
|
|
|
|
|
index_t idx0, idx1, idx2;
|
|
|
|
|
idx0.vertex_index = ind[0].v_idx;
|
|
|
|
|
idx0.normal_index = ind[0].vn_idx;
|
|
|
|
|
idx0.texcoord_index = ind[0].vt_idx;
|
|
|
|
|
idx1.vertex_index = ind[1].v_idx;
|
|
|
|
|
idx1.normal_index = ind[1].vn_idx;
|
|
|
|
|
idx1.texcoord_index = ind[1].vt_idx;
|
|
|
|
|
idx2.vertex_index = ind[2].v_idx;
|
|
|
|
|
idx2.normal_index = ind[2].vn_idx;
|
|
|
|
|
idx2.texcoord_index = ind[2].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);
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove v1 from the list
|
|
|
|
|
size_t removed_vert_index = (guess_vert + 1) % npolys;
|
|
|
|
|
while (removed_vert_index + 1 < npolys) {
|
|
|
|
|
remainingFace.vertex_indices[removed_vert_index] =
|
|
|
|
|
remainingFace.vertex_indices[removed_vert_index + 1];
|
|
|
|
|
removed_vert_index += 1;
|
|
|
|
|
}
|
|
|
|
|
remainingFace.vertex_indices.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove v1 from the list
|
|
|
|
|
size_t removed_vert_index = (guess_vert + 1) % npolys;
|
|
|
|
|
while (removed_vert_index + 1 < npolys) {
|
|
|
|
|
remainingFace.vertex_indices[removed_vert_index] =
|
|
|
|
|
remainingFace.vertex_indices[removed_vert_index + 1];
|
|
|
|
|
removed_vert_index += 1;
|
|
|
|
|
if (remainingFace.vertex_indices.size() == 3) {
|
|
|
|
|
i0 = remainingFace.vertex_indices[0];
|
|
|
|
|
i1 = remainingFace.vertex_indices[1];
|
|
|
|
|
i2 = remainingFace.vertex_indices[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);
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
remainingFace.vertex_indices.pop_back();
|
|
|
|
|
} else {
|
|
|
|
|
for (size_t k = 0; k < npolys; k++) {
|
|
|
|
|
index_t idx;
|
|
|
|
|
idx.vertex_index = face.vertex_indices[k].v_idx;
|
|
|
|
|
idx.normal_index = face.vertex_indices[k].vn_idx;
|
|
|
|
|
idx.texcoord_index = face.vertex_indices[k].vt_idx;
|
|
|
|
|
shape->mesh.indices.push_back(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape->mesh.num_face_vertices.push_back(
|
|
|
|
|
static_cast<unsigned char>(npolys));
|
|
|
|
|
shape->mesh.material_ids.push_back(material_id); // per face
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(
|
|
|
|
|
face.smoothing_group_id); // per face
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (remainingFace.vertex_indices.size() == 3) {
|
|
|
|
|
i0 = remainingFace.vertex_indices[0];
|
|
|
|
|
i1 = remainingFace.vertex_indices[1];
|
|
|
|
|
i2 = remainingFace.vertex_indices[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);
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (size_t k = 0; k < npolys; k++) {
|
|
|
|
|
index_t idx;
|
|
|
|
|
idx.vertex_index = face.vertex_indices[k].v_idx;
|
|
|
|
|
idx.normal_index = face.vertex_indices[k].vn_idx;
|
|
|
|
|
idx.texcoord_index = face.vertex_indices[k].vt_idx;
|
|
|
|
|
shape->mesh.indices.push_back(idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape->mesh.num_face_vertices.push_back(
|
|
|
|
|
static_cast<unsigned char>(npolys));
|
|
|
|
|
shape->mesh.material_ids.push_back(material_id); // per face
|
|
|
|
|
shape->mesh.smoothing_group_ids.push_back(
|
|
|
|
|
face.smoothing_group_id); // per face
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape->name = name;
|
|
|
|
|
shape->mesh.tags = tags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shape->name = name;
|
|
|
|
|
shape->mesh.tags = tags;
|
|
|
|
|
if (!lineGroup.empty()) {
|
|
|
|
|
shape->path.indices.swap(lineGroup);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@@ -1737,14 +1752,13 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string baseDir = mtl_basedir ? mtl_basedir : "";
|
|
|
|
|
if (!baseDir.empty()) {
|
|
|
|
|
if (!baseDir.empty()) {
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
|
const char dirsep = '/';
|
|
|
|
|
#else
|
|
|
|
|
const char dirsep = '\\';
|
|
|
|
|
#endif
|
|
|
|
|
if (baseDir[baseDir.length() - 1] != dirsep)
|
|
|
|
|
baseDir += dirsep;
|
|
|
|
|
if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep;
|
|
|
|
|
}
|
|
|
|
|
MaterialFileReader matFileReader(baseDir);
|
|
|
|
|
|
|
|
|
|
@@ -1764,6 +1778,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
std::vector<real_t> vc;
|
|
|
|
|
std::vector<tag_t> tags;
|
|
|
|
|
std::vector<face_t> faceGroup;
|
|
|
|
|
std::vector<int> lineGroup;
|
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
|
|
// material
|
|
|
|
|
@@ -1776,10 +1791,13 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
|
|
|
|
|
shape_t shape;
|
|
|
|
|
|
|
|
|
|
size_t line_num = 0;
|
|
|
|
|
std::string linebuf;
|
|
|
|
|
while (inStream->peek() != -1) {
|
|
|
|
|
safeGetline(*inStream, linebuf);
|
|
|
|
|
|
|
|
|
|
line_num++;
|
|
|
|
|
|
|
|
|
|
// Trim newline '\r\n' or '\n'
|
|
|
|
|
if (linebuf.size() > 0) {
|
|
|
|
|
if (linebuf[linebuf.size() - 1] == '\n')
|
|
|
|
|
@@ -1841,6 +1859,33 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// line
|
|
|
|
|
if (token[0] == 'l' && IS_SPACE((token[1]))) {
|
|
|
|
|
token += 2;
|
|
|
|
|
|
|
|
|
|
line_t line_cache;
|
|
|
|
|
bool end_line_bit = 0;
|
|
|
|
|
while (!IS_NEW_LINE(token[0])) {
|
|
|
|
|
// get index from string
|
|
|
|
|
int idx;
|
|
|
|
|
fixIndex(parseInt(&token), 0, &idx);
|
|
|
|
|
|
|
|
|
|
size_t n = strspn(token, " \t\r");
|
|
|
|
|
token += n;
|
|
|
|
|
|
|
|
|
|
if (!end_line_bit) {
|
|
|
|
|
line_cache.idx0 = idx;
|
|
|
|
|
} else {
|
|
|
|
|
line_cache.idx1 = idx;
|
|
|
|
|
lineGroup.push_back(line_cache.idx0);
|
|
|
|
|
lineGroup.push_back(line_cache.idx1);
|
|
|
|
|
line_cache = line_t();
|
|
|
|
|
}
|
|
|
|
|
end_line_bit = !end_line_bit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// face
|
|
|
|
|
if (token[0] == 'f' && IS_SPACE((token[1]))) {
|
|
|
|
|
token += 2;
|
|
|
|
|
@@ -1890,9 +1935,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
if (newMaterialId != material) {
|
|
|
|
|
// Create per-face material. Thus we don't add `shape` to `shapes` at
|
|
|
|
|
// this time.
|
|
|
|
|
// just clear `faceGroup` after `exportFaceGroupToShape()` call.
|
|
|
|
|
exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
|
|
|
|
|
triangulate, v);
|
|
|
|
|
// just clear `faceGroup` after `exportGroupsToShape()` call.
|
|
|
|
|
exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material, name,
|
|
|
|
|
triangulate, v);
|
|
|
|
|
faceGroup.clear();
|
|
|
|
|
material = newMaterialId;
|
|
|
|
|
}
|
|
|
|
|
@@ -1946,8 +1991,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
// group name
|
|
|
|
|
if (token[0] == 'g' && IS_SPACE((token[1]))) {
|
|
|
|
|
// flush previous face group.
|
|
|
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
|
|
|
|
|
triangulate, v);
|
|
|
|
|
bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags,
|
|
|
|
|
material, name, triangulate, v);
|
|
|
|
|
(void)ret; // return value not used.
|
|
|
|
|
|
|
|
|
|
if (shape.mesh.indices.size() > 0) {
|
|
|
|
|
@@ -1960,7 +2005,6 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
faceGroup.clear();
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> names;
|
|
|
|
|
names.reserve(2);
|
|
|
|
|
|
|
|
|
|
while (!IS_NEW_LINE(token[0])) {
|
|
|
|
|
std::string str = parseString(&token);
|
|
|
|
|
@@ -1968,13 +2012,31 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
token += strspn(token, " \t\r"); // skip tag
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(names.size() > 0);
|
|
|
|
|
// names[0] must be 'g'
|
|
|
|
|
|
|
|
|
|
// names[0] must be 'g', so skip the 0th element.
|
|
|
|
|
if (names.size() > 1) {
|
|
|
|
|
name = names[1];
|
|
|
|
|
if (names.size() < 2) {
|
|
|
|
|
// 'g' with empty names
|
|
|
|
|
if (err) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "WARN: Empty group name. line: " << line_num << "\n";
|
|
|
|
|
(*err) += ss.str();
|
|
|
|
|
name = "";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
name = "";
|
|
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << names[1];
|
|
|
|
|
|
|
|
|
|
// tinyobjloader does not support multiple groups for a primitive.
|
|
|
|
|
// Currently we concatinate multiple group names with a space to get
|
|
|
|
|
// single group name.
|
|
|
|
|
|
|
|
|
|
for (size_t i = 2; i < names.size(); i++) {
|
|
|
|
|
ss << " " << names[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name = ss.str();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
@@ -1983,8 +2045,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
// object name
|
|
|
|
|
if (token[0] == 'o' && IS_SPACE((token[1]))) {
|
|
|
|
|
// flush previous face group.
|
|
|
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
|
|
|
|
|
triangulate, v);
|
|
|
|
|
bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags,
|
|
|
|
|
material, name, triangulate, v);
|
|
|
|
|
if (ret) {
|
|
|
|
|
shapes->push_back(shape);
|
|
|
|
|
}
|
|
|
|
|
@@ -2003,7 +2065,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (token[0] == 't' && IS_SPACE(token[1])) {
|
|
|
|
|
const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize.
|
|
|
|
|
const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize.
|
|
|
|
|
tag_t tag;
|
|
|
|
|
|
|
|
|
|
token += 2;
|
|
|
|
|
@@ -2091,9 +2153,9 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
|
|
|
|
// Ignore unknown command.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
|
|
|
|
|
triangulate, v);
|
|
|
|
|
// exportFaceGroupToShape return false when `usemtl` is called in the last
|
|
|
|
|
bool ret = exportGroupsToShape(&shape, faceGroup, lineGroup, tags, material,
|
|
|
|
|
name, triangulate, v);
|
|
|
|
|
// exportGroupsToShape return false when `usemtl` is called in the last
|
|
|
|
|
// line.
|
|
|
|
|
// we also add `shape` to `shapes` when `shape.mesh` has already some
|
|
|
|
|
// faces(indices)
|
|
|
|
|
@@ -2128,7 +2190,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
|
|
|
|
|
std::vector<material_t> materials;
|
|
|
|
|
std::vector<std::string> names;
|
|
|
|
|
names.reserve(2);
|
|
|
|
|
std::string name;
|
|
|
|
|
std::vector<const char *> names_out;
|
|
|
|
|
|
|
|
|
|
std::string linebuf;
|
|
|
|
|
@@ -2305,13 +2366,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
|
|
|
|
|
|
|
|
|
|
assert(names.size() > 0);
|
|
|
|
|
|
|
|
|
|
// names[0] must be 'g', so skip the 0th element.
|
|
|
|
|
if (names.size() > 1) {
|
|
|
|
|
name = names[1];
|
|
|
|
|
} else {
|
|
|
|
|
name.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (callback.group_cb) {
|
|
|
|
|
if (names.size() > 1) {
|
|
|
|
|
// create const char* array.
|
|
|
|
|
|