Add per-face smoothing groupd ids to mesh_t.

Add unit test for smoothing groups.
This commit is contained in:
Syoyo Fujita
2018-01-31 22:12:56 +09:00
parent 2dfc37a475
commit dcad3e6c50
4 changed files with 319 additions and 190 deletions

View File

@@ -225,7 +225,10 @@ typedef struct {
// face. 3 = polygon, 4 = quad,
// ... Up to 255.
std::vector<int> material_ids; // per-face material ID
std::vector<tag_t> tags; // SubD tag
std::vector<unsigned int> smoothing_group_ids; // per-face smoothing group
// ID(0 = off. positive value
// = group id)
std::vector<tag_t> tags; // SubD tag
} mesh_t;
typedef struct {
@@ -384,15 +387,14 @@ struct vertex_index_t {
// Internal data structure for face representation
// index + smoothing group.
struct face_t {
int smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off.
unsigned int
smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off.
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) {}
};
struct tag_sizes {
tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
int num_ints;
@@ -680,11 +682,12 @@ static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w,
}
// Extension: parse vertex with colors(6 items)
static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, real_t *r,
real_t *g, real_t *b,
const char **token, const double default_x = 0.0,
const double default_y = 0.0,
const double default_z = 0.0) {
static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z,
real_t *r, real_t *g, real_t *b,
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);
@@ -747,7 +750,7 @@ static tag_sizes parseTagTriple(const char **token) {
return ts;
}
(*token)++; // Skip '/'
(*token)++; // Skip '/'
(*token) += strspn((*token), " \t");
ts.num_reals = atoi((*token));
@@ -755,7 +758,7 @@ static tag_sizes parseTagTriple(const char **token) {
if ((*token)[0] != '/') {
return ts;
}
(*token)++; // Skip '/'
(*token)++; // Skip '/'
ts.num_strings = parseInt(token);
@@ -926,7 +929,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);
@@ -991,22 +994,26 @@ static void InitMaterial(material_t *material) {
}
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
static int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
static int pnpoly(int nvert, float *vertx, float *verty, float testx,
float testy) {
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((verty[i] > testy) != (verty[j] > testy)) &&
(testx <
(vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
vertx[i]))
c = !c;
}
return c;
}
// 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) {
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()) {
return false;
}
@@ -1022,154 +1029,165 @@ static bool exportFaceGroupToShape(
size_t npolys = face.vertex_indices.size();
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);
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;
float cx = std::fabs(e0y*e1z - e0z*e1y);
float cy = std::fabs(e0z*e1x - e0x*e1z);
float cz = std::fabs(e0x*e1y - e0y*e1x);
const float epsilon = 0.0001f;
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;
}
}
// 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);
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;
float cx = std::fabs(e0y * e1z - e0z * e1y);
float cy = std::fabs(e0z * e1x - e0x * e1z);
float cz = std::fabs(e0x * e1y - e0y * e1x);
const float epsilon = 0.0001f;
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;
}
}
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);
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)*0.5f;
}
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);
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) * 0.5f;
}
int maxRounds = 10; // arbitrary max loop count to protect against unexpected errors
int maxRounds =
10; // arbitrary max loop count to protect against unexpected errors
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);
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 < 0.0f ) { guess_vert += 1; continue; }
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);
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 < 0.0f) {
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 ovi = size_t(remainingFace.vertex_indices[(guess_vert+otherVert)%npolys].v_idx);
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;
}
}
// check all other verts in case they are inside this triangle
bool overlap = false;
for (size_t otherVert = 3; otherVert < npolys; ++otherVert) {
size_t ovi = size_t(
remainingFace.vertex_indices[(guess_vert + otherVert) % npolys]
.v_idx);
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; }
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;
// 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.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.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;
}
remainingFace.vertex_indices.pop_back();
}
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;
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.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.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;
@@ -1182,6 +1200,8 @@ static bool exportFaceGroupToShape(
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
}
}
@@ -1690,7 +1710,6 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
std::vector<real_t> vt;
std::vector<real_t> vc;
std::vector<tag_t> tags;
std::vector<int> smoothing_groups;
std::vector<face_t> faceGroup;
std::string name;
@@ -1699,7 +1718,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
int material = -1;
// smoothing group id
int current_smoothing_id = 0; // Initial value. 0 means no smoothing.
unsigned int current_smoothing_id =
0; // Initial value. 0 means no smoothing.
shape_t shape;
@@ -1881,7 +1901,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
shapes->push_back(shape);
}
shape = shape_t();
shape = shape_t();
// material = -1;
faceGroup.clear();
@@ -1956,41 +1976,42 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
tags.push_back(tag);
continue;
}
if (token[0] == 's' && IS_SPACE(token[1])) {
// smoothing group id
token += 2;
// skip space.
token += strspn(token, " \t"); // skip space
if (token[0] == '\0') {
continue;
}
if (token[0] == '\r' || token[1] == '\n') {
continue;
}
if (token[0] == 's' && IS_SPACE(token[1])) {
// smoothing group id
token += 2;
if (strlen(token) >= 3) {
if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') {
current_smoothing_id = 0;
}
} else {
// assume number
int smGroupId = parseInt(&token);
if (smGroupId < 0) {
// ???
// skip space.
token += strspn(token, " \t"); // skip space
if (token[0] == '\0') {
continue;
}
current_smoothing_id = smGroupId;
}
if (token[0] == '\r' || token[1] == '\n') {
continue;
}
continue;
} // smoothing group id
if (strlen(token) >= 3) {
if (token[0] == 'o' && token[1] == 'f' && token[2] == 'f') {
current_smoothing_id = 0;
}
} else {
// assume number
int smGroupId = parseInt(&token);
if (smGroupId < 0) {
// parse error. force set to 0.
// FIXME(syoyo): Report warning.
current_smoothing_id = 0;
} else {
current_smoothing_id = static_cast<unsigned int>(smGroupId);
}
}
continue;
} // smoothing group id
// Ignore unknown command.
}