2017-02-26 20:46:33 +00:00
|
|
|
#include <stdlib.h>
|
2017-02-26 22:13:31 +00:00
|
|
|
#include <string.h>
|
2017-02-26 20:46:33 +00:00
|
|
|
|
2017-08-03 15:31:31 +00:00
|
|
|
#include <boost/nowide/cstdio.hpp>
|
|
|
|
|
2017-02-26 20:46:33 +00:00
|
|
|
#include "objparser.hpp"
|
|
|
|
|
|
|
|
namespace ObjParser {
|
|
|
|
|
|
|
|
static bool obj_parseline(const char *line, ObjData &data)
|
|
|
|
{
|
|
|
|
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
|
|
|
|
|
|
|
|
if (*line == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Ignore whitespaces at the beginning of the line.
|
|
|
|
//FIXME is this a good idea?
|
|
|
|
EATWS();
|
|
|
|
|
|
|
|
char c1 = *line ++;
|
|
|
|
switch (c1) {
|
|
|
|
case '#':
|
|
|
|
// Comment, ignore the rest of the line.
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
{
|
|
|
|
// Parse vertex geometry (position, normal, texture coordinates)
|
|
|
|
char c2 = *line ++;
|
|
|
|
switch (c2) {
|
|
|
|
case 't':
|
|
|
|
{
|
|
|
|
// vt - vertex texture parameter
|
|
|
|
// u v [w], w == 0 (or w == 1)
|
|
|
|
char c2 = *line ++;
|
|
|
|
if (c2 != ' ' && c2 != '\t')
|
|
|
|
return false;
|
|
|
|
EATWS();
|
|
|
|
char *endptr = 0;
|
|
|
|
double u = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double v = 0;
|
|
|
|
if (*line != 0) {
|
|
|
|
v = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
}
|
|
|
|
double w = 0;
|
|
|
|
if (*line != 0) {
|
|
|
|
w = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
}
|
|
|
|
if (*line != 0)
|
|
|
|
return false;
|
|
|
|
data.textureCoordinates.push_back((float)u);
|
|
|
|
data.textureCoordinates.push_back((float)v);
|
|
|
|
data.textureCoordinates.push_back((float)w);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'n':
|
|
|
|
{
|
|
|
|
// vn - vertex normal
|
|
|
|
// x y z
|
|
|
|
char c2 = *line ++;
|
|
|
|
if (c2 != ' ' && c2 != '\t')
|
|
|
|
return false;
|
|
|
|
EATWS();
|
|
|
|
char *endptr = 0;
|
|
|
|
double x = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double y = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double z = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
if (*line != 0)
|
|
|
|
return false;
|
|
|
|
data.normals.push_back((float)x);
|
|
|
|
data.normals.push_back((float)y);
|
|
|
|
data.normals.push_back((float)z);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'p':
|
|
|
|
{
|
|
|
|
// vp - vertex parameter
|
|
|
|
char c2 = *line ++;
|
|
|
|
if (c2 != ' ' && c2 != '\t')
|
|
|
|
return false;
|
|
|
|
EATWS();
|
|
|
|
char *endptr = 0;
|
|
|
|
double u = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double v = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double w = 0;
|
|
|
|
if (*line != 0) {
|
|
|
|
w = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
}
|
|
|
|
if (*line != 0)
|
|
|
|
return false;
|
|
|
|
data.parameters.push_back((float)u);
|
|
|
|
data.parameters.push_back((float)v);
|
|
|
|
data.parameters.push_back((float)w);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
// v - vertex geometry
|
|
|
|
if (c2 != ' ' && c2 != '\t')
|
|
|
|
return false;
|
|
|
|
EATWS();
|
|
|
|
char *endptr = 0;
|
|
|
|
double x = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double y = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double z = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
double w = 1.0;
|
|
|
|
if (*line != 0) {
|
|
|
|
w = strtod(line, &endptr);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
}
|
2019-09-09 09:50:19 +00:00
|
|
|
// the following check is commented out because there may be obj files containing extra data, as those generated by Meshlab,
|
|
|
|
// see https://dev.prusa3d.com/browse/SPE-1019 for an example,
|
|
|
|
// and this would lead to a crash because no vertex would be stored
|
|
|
|
// if (*line != 0)
|
|
|
|
// return false;
|
2019-09-09 07:56:36 +00:00
|
|
|
data.coordinates.push_back((float)x);
|
2017-02-26 20:46:33 +00:00
|
|
|
data.coordinates.push_back((float)y);
|
|
|
|
data.coordinates.push_back((float)z);
|
|
|
|
data.coordinates.push_back((float)w);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'f':
|
|
|
|
{
|
|
|
|
// face
|
|
|
|
EATWS();
|
|
|
|
if (*line == 0)
|
|
|
|
return false;
|
2019-06-25 11:06:04 +00:00
|
|
|
|
2017-02-26 20:46:33 +00:00
|
|
|
// current vertex to be parsed
|
|
|
|
ObjVertex vertex;
|
|
|
|
char *endptr = 0;
|
|
|
|
while (*line != 0) {
|
|
|
|
// Parse a single vertex reference.
|
|
|
|
vertex.coordIdx = 0;
|
|
|
|
vertex.normalIdx = 0;
|
|
|
|
vertex.textureCoordIdx = 0;
|
|
|
|
vertex.coordIdx = strtol(line, &endptr, 10);
|
|
|
|
// Coordinate has to be defined
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
if (*line == '/') {
|
|
|
|
++ line;
|
|
|
|
// Texture coordinate index may be missing after a 1st slash, but then the normal index has to be present.
|
|
|
|
if (*line != '/') {
|
|
|
|
// Parse the texture coordinate index.
|
|
|
|
vertex.textureCoordIdx = strtol(line, &endptr, 10);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
}
|
|
|
|
if (*line == '/') {
|
|
|
|
// Parse normal index.
|
|
|
|
++ line;
|
|
|
|
vertex.normalIdx = strtol(line, &endptr, 10);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (vertex.coordIdx < 0)
|
2019-09-09 07:56:36 +00:00
|
|
|
vertex.coordIdx += (int)data.coordinates.size() / 4;
|
|
|
|
else
|
2017-02-26 20:46:33 +00:00
|
|
|
-- vertex.coordIdx;
|
|
|
|
if (vertex.normalIdx < 0)
|
2019-09-09 07:56:36 +00:00
|
|
|
vertex.normalIdx += (int)data.normals.size() / 3;
|
|
|
|
else
|
2017-02-26 20:46:33 +00:00
|
|
|
-- vertex.normalIdx;
|
|
|
|
if (vertex.textureCoordIdx < 0)
|
2019-09-09 07:56:36 +00:00
|
|
|
vertex.textureCoordIdx += (int)data.textureCoordinates.size() / 3;
|
|
|
|
else
|
2017-02-26 20:46:33 +00:00
|
|
|
-- vertex.textureCoordIdx;
|
|
|
|
data.vertices.push_back(vertex);
|
|
|
|
EATWS();
|
|
|
|
}
|
|
|
|
vertex.coordIdx = -1;
|
|
|
|
vertex.normalIdx = -1;
|
|
|
|
vertex.textureCoordIdx = -1;
|
|
|
|
data.vertices.push_back(vertex);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'm':
|
|
|
|
{
|
|
|
|
if (*(line ++) != 't' ||
|
|
|
|
*(line ++) != 'l' ||
|
|
|
|
*(line ++) != 'l' ||
|
|
|
|
*(line ++) != 'i' ||
|
|
|
|
*(line ++) != 'b')
|
|
|
|
return false;
|
|
|
|
// mtllib [external .mtl file name]
|
|
|
|
// printf("mtllib %s\r\n", line);
|
|
|
|
EATWS();
|
|
|
|
data.mtllibs.push_back(std::string(line));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'u':
|
|
|
|
{
|
|
|
|
if (*(line ++) != 's' ||
|
|
|
|
*(line ++) != 'e' ||
|
|
|
|
*(line ++) != 'm' ||
|
|
|
|
*(line ++) != 't' ||
|
|
|
|
*(line ++) != 'l')
|
|
|
|
return false;
|
|
|
|
// usemtl [material name]
|
|
|
|
// printf("usemtl %s\r\n", line);
|
|
|
|
EATWS();
|
|
|
|
ObjUseMtl usemtl;
|
2019-09-09 07:56:36 +00:00
|
|
|
usemtl.vertexIdxFirst = (int)data.vertices.size();
|
|
|
|
usemtl.name = line;
|
2017-02-26 20:46:33 +00:00
|
|
|
data.usemtls.push_back(usemtl);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'o':
|
|
|
|
{
|
|
|
|
// o [object name]
|
|
|
|
EATWS();
|
|
|
|
while (*line != ' ' && *line != '\t' && *line != 0)
|
|
|
|
++ line;
|
|
|
|
// copy name to line.
|
|
|
|
EATWS();
|
|
|
|
if (*line != 0)
|
|
|
|
return false;
|
|
|
|
ObjObject object;
|
2019-09-09 07:56:36 +00:00
|
|
|
object.vertexIdxFirst = (int)data.vertices.size();
|
|
|
|
object.name = line;
|
2017-02-26 20:46:33 +00:00
|
|
|
data.objects.push_back(object);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'g':
|
|
|
|
{
|
|
|
|
// g [group name]
|
|
|
|
// printf("group %s\r\n", line);
|
|
|
|
ObjGroup group;
|
2019-09-09 07:56:36 +00:00
|
|
|
group.vertexIdxFirst = (int)data.vertices.size();
|
|
|
|
group.name = line;
|
2017-02-26 20:46:33 +00:00
|
|
|
data.groups.push_back(group);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's':
|
|
|
|
{
|
|
|
|
// s 1 / off
|
|
|
|
char c2 = *line ++;
|
|
|
|
if (c2 != ' ' && c2 != '\t')
|
|
|
|
return false;
|
|
|
|
EATWS();
|
|
|
|
char *endptr = 0;
|
|
|
|
long g = strtol(line, &endptr, 10);
|
|
|
|
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
|
|
|
return false;
|
|
|
|
line = endptr;
|
|
|
|
EATWS();
|
|
|
|
if (*line != 0)
|
|
|
|
return false;
|
|
|
|
ObjSmoothingGroup group;
|
2019-09-09 07:56:36 +00:00
|
|
|
group.vertexIdxFirst = (int)data.vertices.size();
|
|
|
|
group.smoothingGroupID = g;
|
2017-02-26 20:46:33 +00:00
|
|
|
data.smoothingGroups.push_back(group);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
printf("ObjParser: Unknown command: %c\r\n", c1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool objparse(const char *path, ObjData &data)
|
|
|
|
{
|
2017-08-03 15:31:31 +00:00
|
|
|
FILE *pFile = boost::nowide::fopen(path, "rt");
|
2017-02-26 20:46:33 +00:00
|
|
|
if (pFile == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
char buf[65536 * 2];
|
|
|
|
size_t len = 0;
|
|
|
|
size_t lenPrev = 0;
|
|
|
|
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
|
|
|
|
len += lenPrev;
|
|
|
|
size_t lastLine = 0;
|
|
|
|
for (size_t i = 0; i < len; ++ i)
|
|
|
|
if (buf[i] == '\r' || buf[i] == '\n') {
|
|
|
|
buf[i] = 0;
|
|
|
|
char *c = buf + lastLine;
|
|
|
|
while (*c == ' ' || *c == '\t')
|
|
|
|
++ c;
|
|
|
|
obj_parseline(c, data);
|
|
|
|
lastLine = i + 1;
|
|
|
|
}
|
|
|
|
lenPrev = len - lastLine;
|
|
|
|
memmove(buf, buf + lastLine, lenPrev);
|
|
|
|
}
|
2019-09-09 07:56:36 +00:00
|
|
|
}
|
|
|
|
catch (std::bad_alloc&) {
|
|
|
|
printf("Out of memory\r\n");
|
2017-02-26 20:46:33 +00:00
|
|
|
}
|
|
|
|
::fclose(pFile);
|
|
|
|
|
2017-04-03 08:05:22 +00:00
|
|
|
// printf("vertices: %d\r\n", data.vertices.size() / 4);
|
|
|
|
// printf("coords: %d\r\n", data.coordinates.size());
|
2017-02-26 20:46:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool savevector(FILE *pFile, const std::vector<T> &v)
|
|
|
|
{
|
|
|
|
size_t cnt = v.size();
|
|
|
|
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
|
|
|
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
|
|
|
if (! v.empty())
|
|
|
|
::fwrite(&v.front(), 1, sizeof(T) * cnt, pFile);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool savevector(FILE *pFile, const std::vector<std::string> &v)
|
|
|
|
{
|
|
|
|
size_t cnt = v.size();
|
|
|
|
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
|
|
|
for (size_t i = 0; i < cnt; ++ i) {
|
|
|
|
size_t len = v[i].size();
|
|
|
|
::fwrite(&len, 1, sizeof(cnt), pFile);
|
|
|
|
::fwrite(v[i].c_str(), 1, len, pFile);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool savevectornameidx(FILE *pFile, const std::vector<T> &v)
|
|
|
|
{
|
|
|
|
size_t cnt = v.size();
|
|
|
|
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
|
|
|
for (size_t i = 0; i < cnt; ++ i) {
|
|
|
|
::fwrite(&v[i].vertexIdxFirst, 1, sizeof(int), pFile);
|
|
|
|
size_t len = v[i].name.size();
|
|
|
|
::fwrite(&len, 1, sizeof(cnt), pFile);
|
|
|
|
::fwrite(v[i].name.c_str(), 1, len, pFile);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool loadvector(FILE *pFile, std::vector<T> &v)
|
|
|
|
{
|
|
|
|
v.clear();
|
|
|
|
size_t cnt = 0;
|
|
|
|
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
|
|
|
if (cnt != 0) {
|
|
|
|
v.assign(cnt, T());
|
|
|
|
if (::fread(&v.front(), sizeof(T), cnt, pFile) != cnt)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadvector(FILE *pFile, std::vector<std::string> &v)
|
|
|
|
{
|
|
|
|
v.clear();
|
|
|
|
size_t cnt = 0;
|
|
|
|
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
v.reserve(cnt);
|
|
|
|
for (size_t i = 0; i < cnt; ++ i) {
|
|
|
|
size_t len = 0;
|
|
|
|
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
std::string s(" ", len);
|
|
|
|
if (::fread(const_cast<char*>(s.c_str()), 1, len, pFile) != len)
|
|
|
|
return false;
|
|
|
|
v.push_back(std::move(s));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
|
|
|
|
{
|
|
|
|
v.clear();
|
|
|
|
size_t cnt = 0;
|
|
|
|
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
v.assign(cnt, T());
|
|
|
|
for (size_t i = 0; i < cnt; ++ i) {
|
|
|
|
if (::fread(&v[i].vertexIdxFirst, sizeof(int), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
size_t len = 0;
|
|
|
|
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
v[i].name.assign(" ", len);
|
|
|
|
if (::fread(const_cast<char*>(v[i].name.c_str()), 1, len, pFile) != len)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool objbinsave(const char *path, const ObjData &data)
|
|
|
|
{
|
2017-08-03 15:31:31 +00:00
|
|
|
FILE *pFile = boost::nowide::fopen(path, "wb");
|
2017-02-26 20:46:33 +00:00
|
|
|
if (pFile == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size_t version = 1;
|
|
|
|
::fwrite(&version, 1, sizeof(version), pFile);
|
|
|
|
|
|
|
|
bool result =
|
|
|
|
savevector(pFile, data.coordinates) &&
|
|
|
|
savevector(pFile, data.textureCoordinates) &&
|
|
|
|
savevector(pFile, data.normals) &&
|
|
|
|
savevector(pFile, data.parameters) &&
|
|
|
|
savevector(pFile, data.mtllibs) &&
|
|
|
|
savevectornameidx(pFile, data.usemtls) &&
|
|
|
|
savevectornameidx(pFile, data.objects) &&
|
|
|
|
savevectornameidx(pFile, data.groups) &&
|
|
|
|
savevector(pFile, data.smoothingGroups) &&
|
|
|
|
savevector(pFile, data.vertices);
|
|
|
|
|
|
|
|
::fclose(pFile);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool objbinload(const char *path, ObjData &data)
|
|
|
|
{
|
2017-08-03 15:31:31 +00:00
|
|
|
FILE *pFile = boost::nowide::fopen(path, "rb");
|
2017-02-26 20:46:33 +00:00
|
|
|
if (pFile == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
data.version = 0;
|
|
|
|
if (::fread(&data.version, sizeof(data.version), 1, pFile) != 1)
|
|
|
|
return false;
|
|
|
|
if (data.version != 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool result =
|
|
|
|
loadvector(pFile, data.coordinates) &&
|
|
|
|
loadvector(pFile, data.textureCoordinates) &&
|
|
|
|
loadvector(pFile, data.normals) &&
|
|
|
|
loadvector(pFile, data.parameters) &&
|
|
|
|
loadvector(pFile, data.mtllibs) &&
|
|
|
|
loadvectornameidx(pFile, data.usemtls) &&
|
|
|
|
loadvectornameidx(pFile, data.objects) &&
|
|
|
|
loadvectornameidx(pFile, data.groups) &&
|
|
|
|
loadvector(pFile, data.smoothingGroups) &&
|
|
|
|
loadvector(pFile, data.vertices);
|
|
|
|
|
|
|
|
::fclose(pFile);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool vectorequal(const std::vector<T> &v1, const std::vector<T> &v2)
|
|
|
|
{
|
|
|
|
if (v1.size() != v2.size())
|
|
|
|
return false;
|
|
|
|
for (size_t i = 0; i < v1.size(); ++ i)
|
|
|
|
if (! (v1[i] == v2[i]))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool vectorequal(const std::vector<std::string> &v1, const std::vector<std::string> &v2)
|
|
|
|
{
|
|
|
|
if (v1.size() != v2.size())
|
|
|
|
return false;
|
|
|
|
for (size_t i = 0; i < v1.size(); ++ i)
|
|
|
|
if (v1[i].compare(v2[i]) != 0)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern bool objequal(const ObjData &data1, const ObjData &data2)
|
|
|
|
{
|
|
|
|
//FIXME ignore version number
|
|
|
|
// version;
|
|
|
|
|
|
|
|
return
|
|
|
|
vectorequal(data1.coordinates, data2.coordinates) &&
|
|
|
|
vectorequal(data1.textureCoordinates, data2.textureCoordinates) &&
|
|
|
|
vectorequal(data1.normals, data2.normals) &&
|
|
|
|
vectorequal(data1.parameters, data2.parameters) &&
|
|
|
|
vectorequal(data1.mtllibs, data2.mtllibs) &&
|
|
|
|
vectorequal(data1.usemtls, data2.usemtls) &&
|
|
|
|
vectorequal(data1.objects, data2.objects) &&
|
|
|
|
vectorequal(data1.groups, data2.groups) &&
|
|
|
|
vectorequal(data1.vertices, data2.vertices);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ObjParser
|