mirror of
https://github.com/bulletphysics/bullet3
synced 2024-12-14 22:00:05 +00:00
884 lines
18 KiB
C++
884 lines
18 KiB
C++
//
|
|
// Copyright 2012-2013, Syoyo Fujita.
|
|
//
|
|
// Licensed under 2-clause BSD liecense.
|
|
//
|
|
|
|
// Erwin Coumans: improved performance, especially in debug mode on Visual Studio (25sec -> 4sec)
|
|
//
|
|
// version 0.9.5: Parse multiple group name.
|
|
// Add support of specifying the base path to load material file.
|
|
// version 0.9.4: Initial suupport of group tag(g)
|
|
// version 0.9.3: Fix parsing triple 'x/y/z'
|
|
// version 0.9.2: Add more .mtl load support
|
|
// version 0.9.1: Add initial .mtl load support
|
|
// version 0.9.0: Initial
|
|
//
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cassert>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#ifdef USE_STREAM
|
|
#include <fstream>
|
|
#else
|
|
#include "../../CommonInterfaces/CommonFileIOInterface.h"
|
|
#endif
|
|
#include <sstream>
|
|
#include "tiny_obj_loader.h"
|
|
#include <stdio.h>
|
|
|
|
namespace tinyobj
|
|
{
|
|
#ifdef USE_STREAM
|
|
//See http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
|
|
std::istream& safeGetline(std::istream& is, std::string& t)
|
|
{
|
|
t.clear();
|
|
|
|
// The characters in the stream are read one-by-one using a std::streambuf.
|
|
// That is faster than reading them one-by-one using the std::istream.
|
|
// Code that uses streambuf this way must be guarded by a sentry object.
|
|
// The sentry object performs various tasks,
|
|
// such as thread synchronization and updating the stream state.
|
|
|
|
std::istream::sentry se(is, true);
|
|
std::streambuf* sb = is.rdbuf();
|
|
|
|
for (;;)
|
|
{
|
|
int c = sb->sbumpc();
|
|
switch (c)
|
|
{
|
|
case '\n':
|
|
return is;
|
|
case '\r':
|
|
if (sb->sgetc() == '\n')
|
|
sb->sbumpc();
|
|
return is;
|
|
case EOF:
|
|
// Also handle the case when the last line has no line ending
|
|
if (t.empty())
|
|
is.setstate(std::ios::eofbit);
|
|
return is;
|
|
default:
|
|
t += (char)c;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static inline bool isSpace(const char c)
|
|
{
|
|
return (c == ' ') || (c == '\t');
|
|
}
|
|
|
|
static inline bool isNewLine(const char c)
|
|
{
|
|
return (c == '\r') || (c == '\n') || (c == '\0');
|
|
}
|
|
|
|
// Make index zero-base, and also support relative index.
|
|
static inline bool fixIndex(int idx, int n, int* ret)
|
|
{
|
|
if (!ret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (idx > 0)
|
|
{
|
|
(*ret) = idx - 1;
|
|
return true;
|
|
}
|
|
|
|
if (idx == 0)
|
|
{
|
|
// zero is not allowed according to the spec.
|
|
return false;
|
|
}
|
|
|
|
if (idx < 0)
|
|
{
|
|
(*ret) = n + idx; // negative value = relative
|
|
return true;
|
|
}
|
|
|
|
return false; // never reach here.
|
|
}
|
|
static inline std::string parseString(const char*& token)
|
|
{
|
|
std::string s;
|
|
int b = strspn(token, " \t");
|
|
int e = strcspn(token, " \t\r");
|
|
s = std::string(&token[b], &token[e]);
|
|
|
|
token += (e - b);
|
|
return s;
|
|
}
|
|
|
|
static inline float parseFloat(const char*& token)
|
|
{
|
|
token += strspn(token, " \t");
|
|
float f = (float)atof(token);
|
|
token += strcspn(token, " \t\r");
|
|
return f;
|
|
}
|
|
|
|
static inline void parseFloat2(
|
|
float& x, float& y,
|
|
const char*& token)
|
|
{
|
|
x = parseFloat(token);
|
|
y = parseFloat(token);
|
|
}
|
|
|
|
static inline void parseFloat3(
|
|
float& x, float& y, float& z,
|
|
const char*& token)
|
|
{
|
|
x = parseFloat(token);
|
|
y = parseFloat(token);
|
|
z = parseFloat(token);
|
|
}
|
|
|
|
// Parse triples with index offsets: i, i/j/k, i//k, i/j
|
|
static bool parseTriple(const char** token, int vsize, int vnsize, int vtsize,
|
|
vertex_index_t* ret)
|
|
{
|
|
if (!ret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vertex_index_t vi(-1);
|
|
|
|
if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
(*token) += strcspn((*token), "/ \t\r");
|
|
if ((*token)[0] != '/')
|
|
{
|
|
(*ret) = vi;
|
|
return true;
|
|
}
|
|
(*token)++;
|
|
|
|
// i//k
|
|
if ((*token)[0] == '/')
|
|
{
|
|
(*token)++;
|
|
if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx)))
|
|
{
|
|
return false;
|
|
}
|
|
(*token) += strcspn((*token), "/ \t\r");
|
|
(*ret) = vi;
|
|
return true;
|
|
}
|
|
|
|
// i/j/k or i/j
|
|
if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
(*token) += strcspn((*token), "/ \t\r");
|
|
if ((*token)[0] != '/')
|
|
{
|
|
(*ret) = vi;
|
|
return true;
|
|
}
|
|
|
|
// i/j/k
|
|
(*token)++; // skip '/'
|
|
if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx)))
|
|
{
|
|
return false;
|
|
}
|
|
(*token) += strcspn((*token), "/ \t\r");
|
|
|
|
(*ret) = vi;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool exportFaceGroupToShape(shape_t* shape, const std::vector<face_t>& face_group,
|
|
const material_t material, const std::string& name,
|
|
const std::vector<float>& v)
|
|
{
|
|
if (face_group.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
shape->name = name;
|
|
// Flattened version of vertex data
|
|
|
|
// Flatten vertices and indices
|
|
for (size_t i = 0; i < face_group.size(); i++)
|
|
{
|
|
const face_t& face = face_group[i];
|
|
size_t npolys = face.size();
|
|
|
|
if (npolys < 3)
|
|
{
|
|
// Face must have 3+ vertices.
|
|
continue;
|
|
}
|
|
vertex_index_t i0 = face[0];
|
|
vertex_index_t i1(-1);
|
|
vertex_index_t i2 = face[1];
|
|
|
|
face_t remainingFace = face; // copy
|
|
size_t guess_vert = 0;
|
|
vertex_index_t ind[3];
|
|
|
|
// How many iterations can we do without decreasing the remaining
|
|
// vertices.
|
|
size_t remainingIterations = face.size();
|
|
size_t previousRemainingVertices = remainingFace.size();
|
|
|
|
while (remainingFace.size() > 3 && remainingIterations > 0)
|
|
{
|
|
npolys = remainingFace.size();
|
|
if (guess_vert >= npolys)
|
|
{
|
|
guess_vert -= npolys;
|
|
}
|
|
|
|
if (previousRemainingVertices != npolys)
|
|
{
|
|
// The number of remaining vertices decreased. Reset counters.
|
|
previousRemainingVertices = npolys;
|
|
remainingIterations = npolys;
|
|
}
|
|
else
|
|
{
|
|
// We didn't consume a vertex on previous iteration, reduce the
|
|
// available iterations.
|
|
remainingIterations--;
|
|
}
|
|
|
|
for (size_t k = 0; k < 3; k++)
|
|
{
|
|
ind[k] = remainingFace[(guess_vert + k) % npolys];
|
|
size_t vi = size_t(ind[k].v_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);
|
|
}
|
|
|
|
// remove v1 from the list
|
|
size_t removed_vert_index = (guess_vert + 1) % npolys;
|
|
while (removed_vert_index + 1 < npolys)
|
|
{
|
|
remainingFace[removed_vert_index] =
|
|
remainingFace[removed_vert_index + 1];
|
|
removed_vert_index += 1;
|
|
}
|
|
remainingFace.pop_back();
|
|
}
|
|
|
|
if (remainingFace.size() == 3)
|
|
{
|
|
i0 = remainingFace[0];
|
|
i1 = remainingFace[1];
|
|
i2 = remainingFace[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->material = material;
|
|
return true;
|
|
}
|
|
|
|
void InitMaterial(material_t& material)
|
|
{
|
|
material.name = "";
|
|
material.ambient_texname = "";
|
|
material.diffuse_texname = "";
|
|
material.specular_texname = "";
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
material.ambient[i] = 0.f;
|
|
material.diffuse[i] = 0.f;
|
|
material.specular[i] = 0.f;
|
|
material.transmittance[i] = 0.f;
|
|
material.emission[i] = 0.f;
|
|
}
|
|
material.shininess = 1.f;
|
|
material.transparency = 1.f;
|
|
}
|
|
|
|
std::string LoadMtl(
|
|
std::map<std::string, material_t>& material_map,
|
|
const char* filename,
|
|
const char* mtl_basepath,
|
|
CommonFileIOInterface* fileIO)
|
|
{
|
|
material_map.clear();
|
|
std::stringstream err;
|
|
|
|
std::string filepath;
|
|
|
|
if (mtl_basepath)
|
|
{
|
|
filepath = std::string(mtl_basepath) + std::string(filename);
|
|
}
|
|
else
|
|
{
|
|
filepath = std::string(filename);
|
|
}
|
|
#ifdef USE_STREAM
|
|
std::ifstream ifs(filepath.c_str());
|
|
if (!ifs)
|
|
{
|
|
err << "Cannot open file [" << filepath << "]" << std::endl;
|
|
return err.str();
|
|
}
|
|
#else
|
|
int fileHandle = fileIO->fileOpen(filepath.c_str(), "r");
|
|
if (fileHandle < 0)
|
|
{
|
|
err << "Cannot open file [" << filepath << "]" << std::endl;
|
|
return err.str();
|
|
}
|
|
#endif
|
|
|
|
material_t material;
|
|
|
|
int maxchars = 8192; // Alloc enough size.
|
|
std::vector<char> buf(maxchars); // Alloc enough size.
|
|
#ifdef USE_STREAM
|
|
while (ifs.peek() != -1)
|
|
#else
|
|
char* line = 0;
|
|
do
|
|
#endif
|
|
{
|
|
std::string linebuf;
|
|
#ifdef USE_STREAM
|
|
safeGetline(ifs, linebuf);
|
|
#else
|
|
char tmpBuf[1024];
|
|
line = fileIO->readLine(fileHandle, tmpBuf, 1024);
|
|
if (line)
|
|
{
|
|
linebuf = line;
|
|
}
|
|
#endif
|
|
|
|
// Trim newline '\r\n' or '\r'
|
|
if (linebuf.size() > 0)
|
|
{
|
|
if (linebuf[linebuf.size() - 1] == '\n') linebuf.erase(linebuf.size() - 1);
|
|
}
|
|
if (linebuf.size() > 0)
|
|
{
|
|
if (linebuf[linebuf.size() - 1] == '\r') linebuf.erase(linebuf.size() - 1);
|
|
}
|
|
|
|
// Skip if empty line.
|
|
if (linebuf.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
|
|
// Skip leading space.
|
|
const char* token = linebuf.c_str();
|
|
token += strspn(token, " \t");
|
|
|
|
assert(token);
|
|
if (token[0] == '\0') continue; // empty line
|
|
|
|
if (token[0] == '#') continue; // comment line
|
|
|
|
// new mtl
|
|
if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6])))
|
|
{
|
|
// flush previous material.
|
|
material_map.insert(std::pair<std::string, material_t>(material.name, material));
|
|
|
|
// initial temporary material
|
|
InitMaterial(material);
|
|
|
|
// set new mtl name
|
|
char namebuf[4096];
|
|
token += 7;
|
|
sscanf(token, "%s", namebuf);
|
|
material.name = namebuf;
|
|
continue;
|
|
}
|
|
|
|
// ambient
|
|
if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2])))
|
|
{
|
|
token += 2;
|
|
float r, g, b;
|
|
parseFloat3(r, g, b, token);
|
|
material.ambient[0] = r;
|
|
material.ambient[1] = g;
|
|
material.ambient[2] = b;
|
|
continue;
|
|
}
|
|
|
|
// diffuse
|
|
if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2])))
|
|
{
|
|
token += 2;
|
|
float r, g, b;
|
|
parseFloat3(r, g, b, token);
|
|
material.diffuse[0] = r;
|
|
material.diffuse[1] = g;
|
|
material.diffuse[2] = b;
|
|
continue;
|
|
}
|
|
|
|
// specular
|
|
if (token[0] == 'K' && token[1] == 's' && isSpace((token[2])))
|
|
{
|
|
token += 2;
|
|
float r, g, b;
|
|
parseFloat3(r, g, b, token);
|
|
material.specular[0] = r;
|
|
material.specular[1] = g;
|
|
material.specular[2] = b;
|
|
continue;
|
|
}
|
|
|
|
// specular
|
|
if (token[0] == 'K' && token[1] == 't' && isSpace((token[2])))
|
|
{
|
|
token += 2;
|
|
float r, g, b;
|
|
parseFloat3(r, g, b, token);
|
|
material.specular[0] = r;
|
|
material.specular[1] = g;
|
|
material.specular[2] = b;
|
|
continue;
|
|
}
|
|
|
|
// emission
|
|
if (token[0] == 'K' && token[1] == 'e' && isSpace(token[2]))
|
|
{
|
|
token += 2;
|
|
float r, g, b;
|
|
parseFloat3(r, g, b, token);
|
|
material.emission[0] = r;
|
|
material.emission[1] = g;
|
|
material.emission[2] = b;
|
|
continue;
|
|
}
|
|
|
|
// shininess
|
|
if (token[0] == 'N' && token[1] == 's' && isSpace(token[2]))
|
|
{
|
|
token += 2;
|
|
material.shininess = parseFloat(token);
|
|
continue;
|
|
}
|
|
|
|
// transparency
|
|
if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2]))
|
|
{
|
|
token += 2;
|
|
material.transparency = parseFloat(token);
|
|
continue;
|
|
}
|
|
|
|
// transparency
|
|
if (token[0] == 'd' && isSpace(token[1]))
|
|
{
|
|
token += 1;
|
|
material.transparency = parseFloat(token);
|
|
continue;
|
|
}
|
|
|
|
// ambient texture
|
|
if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6]))
|
|
{
|
|
token += 7;
|
|
material.ambient_texname = token;
|
|
continue;
|
|
}
|
|
|
|
// diffuse texture
|
|
if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6]))
|
|
{
|
|
token += 7;
|
|
material.diffuse_texname = token;
|
|
continue;
|
|
}
|
|
|
|
// specular texture
|
|
if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6]))
|
|
{
|
|
token += 7;
|
|
material.specular_texname = token;
|
|
continue;
|
|
}
|
|
|
|
// normal texture
|
|
if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6]))
|
|
{
|
|
token += 7;
|
|
material.normal_texname = token;
|
|
continue;
|
|
}
|
|
|
|
// unknown parameter
|
|
const char* _space = strchr(token, ' ');
|
|
if (!_space)
|
|
{
|
|
_space = strchr(token, '\t');
|
|
}
|
|
if (_space)
|
|
{
|
|
int len = _space - token;
|
|
std::string key(token, len);
|
|
std::string value = _space + 1;
|
|
material.unknown_parameter.insert(std::pair<std::string, std::string>(key, value));
|
|
}
|
|
}
|
|
#ifndef USE_STREAM
|
|
while (line)
|
|
;
|
|
#endif
|
|
// flush last material.
|
|
material_map.insert(std::pair<std::string, material_t>(material.name, material));
|
|
|
|
if (fileHandle >= 0)
|
|
{
|
|
fileIO->fileClose(fileHandle);
|
|
}
|
|
return err.str();
|
|
}
|
|
|
|
std::string
|
|
LoadObj(
|
|
attrib_t& attrib,
|
|
std::vector<shape_t>& shapes,
|
|
const char* filename,
|
|
const char* mtl_basepath,
|
|
CommonFileIOInterface* fileIO)
|
|
{
|
|
attrib.vertices.clear();
|
|
attrib.normals.clear();
|
|
attrib.texcoords.clear();
|
|
shapes.clear();
|
|
std::string tmp = filename;
|
|
if (!mtl_basepath)
|
|
{
|
|
int last_slash = 0;
|
|
for (int c = 0; c < (int)tmp.size(); ++c)
|
|
if (tmp[c] == '/' || tmp[c] == '\\')
|
|
last_slash = c;
|
|
tmp = tmp.substr(0, last_slash);
|
|
mtl_basepath = tmp.c_str();
|
|
//fprintf(stderr, "MTL PATH '%s' orig '%s'\n", mtl_basepath, filename);
|
|
}
|
|
std::stringstream err;
|
|
#ifdef USE_STREAM
|
|
std::ifstream ifs(filename);
|
|
if (!ifs)
|
|
{
|
|
err << "Cannot open file [" << filename << "]" << std::endl;
|
|
return err.str();
|
|
}
|
|
#else
|
|
int fileHandle = fileIO->fileOpen(filename, "r");
|
|
if (fileHandle < 0)
|
|
{
|
|
err << "Cannot open file [" << filename << "]" << std::endl;
|
|
return err.str();
|
|
}
|
|
#endif
|
|
|
|
std::vector<float> v;
|
|
std::vector<float> vn;
|
|
std::vector<float> vt;
|
|
std::string name;
|
|
|
|
int greatest_v_idx = -1;
|
|
int greatest_vn_idx = -1;
|
|
int greatest_vt_idx = -1;
|
|
|
|
std::vector<face_t> faceGroup;
|
|
// material
|
|
std::map<std::string, material_t> material_map;
|
|
material_t material;
|
|
InitMaterial(material);
|
|
|
|
int maxchars = 8192; // Alloc enough size.
|
|
std::vector<char> buf(maxchars); // Alloc enough size.
|
|
std::string linebuf;
|
|
linebuf.reserve(maxchars);
|
|
|
|
#ifdef USE_STREAM
|
|
while (ifs.peek() != -1)
|
|
#else
|
|
char* line = 0;
|
|
do
|
|
#endif
|
|
{
|
|
linebuf.resize(0);
|
|
#ifdef USE_STREAM
|
|
safeGetline(ifs, linebuf);
|
|
#else
|
|
char tmpBuf[1024];
|
|
line = fileIO->readLine(fileHandle, tmpBuf, 1024);
|
|
if (line)
|
|
{
|
|
linebuf = line;
|
|
}
|
|
#endif
|
|
// Trim newline '\r\n' or '\r'
|
|
if (linebuf.size() > 0)
|
|
{
|
|
if (linebuf[linebuf.size() - 1] == '\n') linebuf.erase(linebuf.size() - 1);
|
|
}
|
|
if (linebuf.size() > 0)
|
|
{
|
|
if (linebuf[linebuf.size() - 1] == '\n') linebuf.erase(linebuf.size() - 1);
|
|
}
|
|
|
|
// Skip if empty line.
|
|
if (linebuf.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Skip leading space.
|
|
const char* token = linebuf.c_str();
|
|
token += strspn(token, " \t");
|
|
|
|
assert(token);
|
|
if (token[0] == '\0') continue; // empty line
|
|
|
|
if (token[0] == '#') continue; // comment line
|
|
|
|
// vertex
|
|
if (token[0] == 'v' && isSpace((token[1])))
|
|
{
|
|
token += 2;
|
|
float x, y, z;
|
|
parseFloat3(x, y, z, token);
|
|
v.push_back(x);
|
|
v.push_back(y);
|
|
v.push_back(z);
|
|
continue;
|
|
}
|
|
|
|
// normal
|
|
if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2])))
|
|
{
|
|
token += 3;
|
|
float x, y, z;
|
|
parseFloat3(x, y, z, token);
|
|
vn.push_back(x);
|
|
vn.push_back(y);
|
|
vn.push_back(z);
|
|
continue;
|
|
}
|
|
|
|
// texcoord
|
|
if (token[0] == 'v' && token[1] == 't' && isSpace((token[2])))
|
|
{
|
|
token += 3;
|
|
float x, y;
|
|
parseFloat2(x, y, token);
|
|
vt.push_back(x);
|
|
vt.push_back(y);
|
|
continue;
|
|
}
|
|
|
|
// face
|
|
if (token[0] == 'f' && isSpace((token[1])))
|
|
{
|
|
token += 2;
|
|
token += strspn(token, " \t");
|
|
|
|
face_t face;
|
|
|
|
face.reserve(3);
|
|
|
|
while (!isNewLine(token[0]))
|
|
{
|
|
vertex_index_t vi;
|
|
if (!parseTriple(&token, static_cast<int>(v.size() / 3),
|
|
static_cast<int>(vn.size() / 3),
|
|
static_cast<int>(vt.size() / 2), &vi))
|
|
{
|
|
err << "Failed parse `f' line(e.g. zero value for face index.";
|
|
return err.str();
|
|
}
|
|
|
|
greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx;
|
|
greatest_vn_idx =
|
|
greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx;
|
|
greatest_vt_idx =
|
|
greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx;
|
|
|
|
face.push_back(vi);
|
|
size_t n = strspn(token, " \t\r");
|
|
token += n;
|
|
}
|
|
faceGroup.push_back(face);
|
|
continue;
|
|
}
|
|
// use mtl
|
|
if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6])))
|
|
{
|
|
char namebuf[4096];
|
|
token += 7;
|
|
sscanf(token, "%s", namebuf);
|
|
|
|
if (material_map.find(namebuf) != material_map.end())
|
|
{
|
|
material = material_map[namebuf];
|
|
}
|
|
else
|
|
{
|
|
// { error!! material not found }
|
|
InitMaterial(material);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// load mtl
|
|
if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6])))
|
|
{
|
|
char namebuf[4096];
|
|
token += 7;
|
|
sscanf(token, "%s", namebuf);
|
|
|
|
std::string err_mtl = LoadMtl(material_map, namebuf, mtl_basepath, fileIO);
|
|
if (!err_mtl.empty())
|
|
{
|
|
//face_group.resize(0); // for safety
|
|
//return err_mtl;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// group name
|
|
if (token[0] == 'g' && isSpace((token[1])))
|
|
{
|
|
// flush previous face group.
|
|
shape_t shape;
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, material, name, v);
|
|
if (ret)
|
|
{
|
|
shapes.push_back(shape);
|
|
}
|
|
|
|
faceGroup.resize(0);
|
|
|
|
std::vector<std::string> names;
|
|
while (!isNewLine(token[0]))
|
|
{
|
|
std::string str = parseString(token);
|
|
names.push_back(str);
|
|
token += strspn(token, " \t\r"); // skip tag
|
|
}
|
|
|
|
assert(names.size() > 0);
|
|
|
|
// names[0] must be 'g', so skipt 0th element.
|
|
if (names.size() > 1)
|
|
{
|
|
name = names[1];
|
|
}
|
|
else
|
|
{
|
|
name = "";
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// object name
|
|
if (token[0] == 'o' && isSpace((token[1])))
|
|
{
|
|
// flush previous face group.
|
|
shape_t shape;
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, material, name, v);
|
|
if (ret)
|
|
{
|
|
shapes.push_back(shape);
|
|
}
|
|
|
|
faceGroup.resize(0);
|
|
|
|
// @todo { multiple object name? }
|
|
char namebuf[4096];
|
|
token += 2;
|
|
sscanf(token, "%s", namebuf);
|
|
name = std::string(namebuf);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Ignore unknown command.
|
|
}
|
|
#ifndef USE_STREAM
|
|
while (line)
|
|
;
|
|
#endif
|
|
|
|
shape_t shape;
|
|
bool ret = exportFaceGroupToShape(&shape, faceGroup, material, name, v);
|
|
if (ret)
|
|
{
|
|
shapes.push_back(shape);
|
|
}
|
|
faceGroup.resize(0); // for safety
|
|
|
|
attrib.vertices.swap(v);
|
|
attrib.normals.swap(vn);
|
|
attrib.texcoords.swap(vt);
|
|
|
|
if (fileHandle >= 0)
|
|
{
|
|
fileIO->fileClose(fileHandle);
|
|
}
|
|
return err.str();
|
|
}
|
|
|
|
}; // namespace tinyobj
|