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

@@ -0,0 +1,23 @@
newmtl test1
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.5 0.2 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl test2
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0000
Kd 0.2 0.5 0.2
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000

View File

@@ -0,0 +1,51 @@
# cube.obj
#
mtllib issue-162-smoothing-group.mtl
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
usemtl test1
g test1
s 1
f 1/1/1 2/2/1 3/3/1
f 3/3/1 2/2/1 4/4/1
usemtl test2
g test2
s off
f 3/1/2 4/2/2 5/3/2
f 5/3/2 4/2/2 6/4/2
s 3
f 5/4/3 6/3/3 7/2/3
f 7/2/3 6/3/3 8/1/3
s 4
f 7/1/4 8/2/4 1/3/4
f 1/3/4 8/2/4 2/4/4
s 0
f 2/1/5 8/2/5 4/3/5
f 4/3/5 8/2/5 6/4/5
s 6
f 7/1/6 1/2/6 5/3/6
f 5/3/6 1/2/6 3/4/6

View File

@@ -717,6 +717,40 @@ TEST_CASE("texture-name-whitespace", "[Issue145]") {
} }
TEST_CASE("smoothing-group", "[Issue162]") {
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-162-smoothing-group.obj", gMtlBasePath);
if (!err.empty()) {
std::cerr << "[Issue162] " << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(2 == shapes.size());
REQUIRE(2 == shapes[0].mesh.smoothing_group_ids.size());
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[0]);
REQUIRE(1 == shapes[0].mesh.smoothing_group_ids[1]);
REQUIRE(10 == shapes[1].mesh.smoothing_group_ids.size());
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[0]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[1]);
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[2]);
REQUIRE(3 == shapes[1].mesh.smoothing_group_ids[3]);
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[4]);
REQUIRE(4 == shapes[1].mesh.smoothing_group_ids[5]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[6]);
REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[7]);
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[8]);
REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[9]);
}
#if 0 #if 0
int int
main( main(

View File

@@ -225,6 +225,9 @@ typedef struct {
// face. 3 = polygon, 4 = quad, // face. 3 = polygon, 4 = quad,
// ... Up to 255. // ... Up to 255.
std::vector<int> material_ids; // per-face material ID std::vector<int> material_ids; // per-face material ID
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 std::vector<tag_t> tags; // SubD tag
} mesh_t; } mesh_t;
@@ -384,15 +387,14 @@ struct vertex_index_t {
// Internal data structure for face representation // Internal data structure for face representation
// index + smoothing group. // index + smoothing group.
struct face_t { 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_; 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 { struct tag_sizes {
tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {}
int num_ints; int num_ints;
@@ -680,9 +682,10 @@ static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w,
} }
// Extension: parse vertex with colors(6 items) // Extension: parse vertex with colors(6 items)
static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, real_t *r, static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z,
real_t *g, real_t *b, real_t *r, real_t *g, real_t *b,
const char **token, const double default_x = 0.0, const char **token,
const double default_x = 0.0,
const double default_y = 0.0, const double default_y = 0.0,
const double default_z = 0.0) { const double default_z = 0.0) {
(*x) = parseReal(token, default_x); (*x) = parseReal(token, default_x);
@@ -991,22 +994,26 @@ static void InitMaterial(material_t *material) {
} }
// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html // 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; int i, j, c = 0;
for (i = 0, j = nvert - 1; i < nvert; j = i++) { for (i = 0, j = nvert - 1; i < nvert; j = i++) {
if (((verty[i] > testy) != (verty[j] > testy)) && if (((verty[i] > testy) != (verty[j] > testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) ) (testx <
(vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) +
vertx[i]))
c = !c; c = !c;
} }
return c; return c;
} }
// TODO(syoyo): refactor function. // TODO(syoyo): refactor function.
static bool exportFaceGroupToShape( static bool exportFaceGroupToShape(shape_t *shape,
shape_t *shape, const std::vector<face_t> &faceGroup, const std::vector<face_t> &faceGroup,
const std::vector<tag_t> &tags, const int material_id, const std::vector<tag_t> &tags,
const std::string &name, bool triangulate, const std::vector<real_t> &v) { const int material_id,
const std::string &name, bool triangulate,
const std::vector<real_t> &v) {
if (faceGroup.empty()) { if (faceGroup.empty()) {
return false; return false;
} }
@@ -1055,8 +1062,7 @@ static bool exportFaceGroupToShape(
if (cx > cy && cx > cz) { if (cx > cy && cx > cz) {
} else { } else {
axes[0] = 0; axes[0] = 0;
if( cz > cx && cz > cy ) if (cz > cx && cz > cy) axes[1] = 1;
axes[1] = 1;
} }
break; break;
} }
@@ -1075,7 +1081,8 @@ static bool exportFaceGroupToShape(
area += (v0x * v1y - v0y * v1x) * 0.5f; 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 face_t remainingFace = face; // copy
size_t guess_vert = 0; size_t guess_vert = 0;
@@ -1100,12 +1107,17 @@ static bool exportFaceGroupToShape(
real_t e1y = vy[2] - vy[1]; real_t e1y = vy[2] - vy[1];
real_t cross = e0x * e1y - e0y * e1x; real_t cross = e0x * e1y - e0y * e1x;
// if an internal angle // if an internal angle
if( cross * area < 0.0f ) { guess_vert += 1; continue; } if (cross * area < 0.0f) {
guess_vert += 1;
continue;
}
// check all other verts in case they are inside this triangle // check all other verts in case they are inside this triangle
bool overlap = false; bool overlap = false;
for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { for (size_t otherVert = 3; otherVert < npolys; ++otherVert) {
size_t ovi = size_t(remainingFace.vertex_indices[(guess_vert+otherVert)%npolys].v_idx); size_t ovi = size_t(
remainingFace.vertex_indices[(guess_vert + otherVert) % npolys]
.v_idx);
real_t tx = v[ovi * 3 + axes[0]]; real_t tx = v[ovi * 3 + axes[0]];
real_t ty = v[ovi * 3 + axes[1]]; real_t ty = v[ovi * 3 + axes[1]];
if (pnpoly(3, vx, vy, tx, ty)) { if (pnpoly(3, vx, vy, tx, ty)) {
@@ -1114,7 +1126,10 @@ static bool exportFaceGroupToShape(
} }
} }
if( overlap ) { guess_vert += 1; continue; } if (overlap) {
guess_vert += 1;
continue;
}
// this triangle is an ear // this triangle is an ear
{ {
@@ -1135,12 +1150,14 @@ static bool exportFaceGroupToShape(
shape->mesh.num_face_vertices.push_back(3); shape->mesh.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id); shape->mesh.material_ids.push_back(material_id);
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
} }
// remove v1 from the list // remove v1 from the list
size_t removed_vert_index = (guess_vert + 1) % npolys; size_t removed_vert_index = (guess_vert + 1) % npolys;
while (removed_vert_index + 1 < npolys) { while (removed_vert_index + 1 < npolys) {
remainingFace.vertex_indices[removed_vert_index] = remainingFace.vertex_indices[removed_vert_index+1]; remainingFace.vertex_indices[removed_vert_index] =
remainingFace.vertex_indices[removed_vert_index + 1];
removed_vert_index += 1; removed_vert_index += 1;
} }
remainingFace.vertex_indices.pop_back(); remainingFace.vertex_indices.pop_back();
@@ -1168,6 +1185,7 @@ static bool exportFaceGroupToShape(
shape->mesh.num_face_vertices.push_back(3); shape->mesh.num_face_vertices.push_back(3);
shape->mesh.material_ids.push_back(material_id); shape->mesh.material_ids.push_back(material_id);
shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id);
} }
} }
} else { } else {
@@ -1182,6 +1200,8 @@ static bool exportFaceGroupToShape(
shape->mesh.num_face_vertices.push_back( shape->mesh.num_face_vertices.push_back(
static_cast<unsigned char>(npolys)); static_cast<unsigned char>(npolys));
shape->mesh.material_ids.push_back(material_id); // per face 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> vt;
std::vector<real_t> vc; std::vector<real_t> vc;
std::vector<tag_t> tags; std::vector<tag_t> tags;
std::vector<int> smoothing_groups;
std::vector<face_t> faceGroup; std::vector<face_t> faceGroup;
std::string name; std::string name;
@@ -1699,7 +1718,8 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
int material = -1; int material = -1;
// smoothing group id // 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; shape_t shape;
@@ -1982,11 +2002,12 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
// assume number // assume number
int smGroupId = parseInt(&token); int smGroupId = parseInt(&token);
if (smGroupId < 0) { if (smGroupId < 0) {
// ??? // parse error. force set to 0.
continue; // FIXME(syoyo): Report warning.
current_smoothing_id = 0;
} else {
current_smoothing_id = static_cast<unsigned int>(smGroupId);
} }
current_smoothing_id = smGroupId;
} }
continue; continue;