Merge branch 'master' of github.com:syoyo/tinyobjloader
This commit is contained in:
@@ -137,8 +137,10 @@ static void PrintInfo(const tinyobj::attrib_t& attrib,
|
||||
for (size_t i = 0; i < shapes.size(); i++) {
|
||||
printf("shape[%ld].name = %s\n", static_cast<long>(i),
|
||||
shapes[i].name.c_str());
|
||||
printf("Size of shape[%ld].indices: %lu\n", static_cast<long>(i),
|
||||
printf("Size of shape[%ld].mesh.indices: %lu\n", static_cast<long>(i),
|
||||
static_cast<unsigned long>(shapes[i].mesh.indices.size()));
|
||||
printf("Size of shape[%ld].path.indices: %lu\n", static_cast<long>(i),
|
||||
static_cast<unsigned long>(shapes[i].path.indices.size()));
|
||||
|
||||
size_t index_offset = 0;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ v 2.000000 2.000000 0.000000
|
||||
g front cube
|
||||
usemtl white
|
||||
f 1 2 3 4
|
||||
# two white spaces between 'back' and 'cube'
|
||||
g back cube
|
||||
# expects white material
|
||||
f 8 7 6 5
|
||||
|
||||
16
models/line-prim.obj
Normal file
16
models/line-prim.obj
Normal file
@@ -0,0 +1,16 @@
|
||||
mtllib cube.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 g0
|
||||
usemtl white
|
||||
l 1 2 3 4
|
||||
l 5 6 7
|
||||
@@ -790,6 +790,41 @@ TEST_CASE("Empty mtl basedir", "[Issue177]") {
|
||||
REQUIRE(true == ret);
|
||||
}
|
||||
|
||||
TEST_CASE("line-primitive", "[line]") {
|
||||
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/line-prim.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << "[line] " << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(1 == shapes.size());
|
||||
REQUIRE(6 == shapes[0].path.indices.size());
|
||||
}
|
||||
|
||||
TEST_CASE("multiple-group-names", "[group]") {
|
||||
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/cube.obj", gMtlBasePath);
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << "[group] " << err << std::endl;
|
||||
}
|
||||
|
||||
REQUIRE(true == ret);
|
||||
REQUIRE(6 == shapes.size());
|
||||
REQUIRE(0 == shapes[0].name.compare("front cube"));
|
||||
REQUIRE(0 == shapes[1].name.compare("back cube")); // multiple whitespaces are aggregated as single white space.
|
||||
}
|
||||
|
||||
// Fuzzer test.
|
||||
// Just check if it does not crash.
|
||||
// Disable by default since Windows filesystem can't create filename of afl testdata
|
||||
|
||||
@@ -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;
|
||||
@@ -1001,8 +1013,7 @@ 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) {
|
||||
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,16 +1026,18 @@ static int pnpoly(int nvert, T *vertx, T *verty, T testx,
|
||||
}
|
||||
|
||||
// TODO(syoyo): refactor function.
|
||||
static bool exportFaceGroupToShape(shape_t *shape,
|
||||
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 int material_id, const std::string &name,
|
||||
bool triangulate,
|
||||
const std::vector<real_t> &v) {
|
||||
if (faceGroup.empty()) {
|
||||
if (faceGroup.empty() && lineGroup.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!faceGroup.empty()) {
|
||||
// Flatten vertices and indices
|
||||
for (size_t i = 0; i < faceGroup.size(); i++) {
|
||||
const face_t &face = faceGroup[i];
|
||||
@@ -1051,8 +1064,7 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
||||
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()) ||
|
||||
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?
|
||||
@@ -1108,8 +1120,8 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
||||
area += (v0x * v1y - v0y * v1x) * static_cast<real_t>(0.5);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1156,9 +1168,7 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t ovi = size_t(
|
||||
remainingFace.vertex_indices[idx]
|
||||
.v_idx);
|
||||
size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx);
|
||||
|
||||
if (((ovi * 3 + axes[0]) >= v.size()) ||
|
||||
((ovi * 3 + axes[1]) >= v.size())) {
|
||||
@@ -1254,6 +1264,11 @@ static bool exportFaceGroupToShape(shape_t *shape,
|
||||
|
||||
shape->name = name;
|
||||
shape->mesh.tags = tags;
|
||||
}
|
||||
|
||||
if (!lineGroup.empty()) {
|
||||
shape->path.indices.swap(lineGroup);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1743,8 +1758,7 @@ bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
|
||||
#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,8 +1935,8 @@ 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,
|
||||
// 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,14 +2012,32 @@ 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];
|
||||
} else {
|
||||
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 {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user