mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-27 15:29:26 -06:00
changing obj loader to tinyobj
This commit is contained in:
@@ -69,6 +69,8 @@ private:
|
||||
|
||||
std::string _source;
|
||||
std::string _destination;
|
||||
|
||||
properties::BoolProperty _performShading;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define __WAVEFRONTOBJECT_H__
|
||||
|
||||
#include <openspace/rendering/model/modelgeometry.h>
|
||||
#include <openspace/util/tiny_obj_loader.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -51,15 +52,20 @@ public:
|
||||
//GLubyte padding[4]; // Pads the struct out to 64 bytes for performance increase
|
||||
} Vertex;
|
||||
|
||||
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
protected:
|
||||
void loadObj(const char *filename);
|
||||
private:
|
||||
void createSphere();
|
||||
|
||||
GLuint _vaoID = 6;
|
||||
GLuint _vBufferID = 7;
|
||||
GLuint _iBufferID = 8;
|
||||
GLuint _vaoID;
|
||||
GLuint _vBufferID;
|
||||
GLuint _iBufferID;
|
||||
|
||||
GLuint _tBufferID;
|
||||
unsigned int _tsize;
|
||||
float* _tarray;
|
||||
|
||||
unsigned int _isize;
|
||||
unsigned int _vsize;
|
||||
|
||||
94
include/openspace/util/tiny_obj_loader.h
Normal file
94
include/openspace/util/tiny_obj_loader.h
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Copyright 2012-2013, Syoyo Fujita.
|
||||
//
|
||||
// Licensed under 2-clause BSD liecense.
|
||||
//
|
||||
#ifndef _TINY_OBJ_LOADER_H
|
||||
#define _TINY_OBJ_LOADER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace tinyobj {
|
||||
|
||||
typedef struct {
|
||||
std::string name;
|
||||
|
||||
float ambient[3];
|
||||
float diffuse[3];
|
||||
float specular[3];
|
||||
float transmittance[3];
|
||||
float emission[3];
|
||||
float shininess;
|
||||
float ior; // index of refraction
|
||||
float dissolve; // 1 == opaque; 0 == fully transparent
|
||||
// illumination model (see http://www.fileformat.info/format/material/)
|
||||
int illum;
|
||||
|
||||
std::string ambient_texname;
|
||||
std::string diffuse_texname;
|
||||
std::string specular_texname;
|
||||
std::string normal_texname;
|
||||
std::map<std::string, std::string> unknown_parameter;
|
||||
} material_t;
|
||||
|
||||
typedef struct {
|
||||
std::vector<float> positions;
|
||||
std::vector<float> normals;
|
||||
std::vector<float> texcoords;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<int> material_ids; // per-mesh material ID
|
||||
} mesh_t;
|
||||
|
||||
typedef struct {
|
||||
std::string name;
|
||||
mesh_t mesh;
|
||||
} shape_t;
|
||||
|
||||
class MaterialReader {
|
||||
public:
|
||||
MaterialReader() {}
|
||||
virtual ~MaterialReader() {}
|
||||
|
||||
virtual std::string operator()(const std::string &matId,
|
||||
std::vector<material_t> &materials,
|
||||
std::map<std::string, int> &matMap) = 0;
|
||||
};
|
||||
|
||||
class MaterialFileReader : public MaterialReader {
|
||||
public:
|
||||
MaterialFileReader(const std::string &mtl_basepath)
|
||||
: m_mtlBasePath(mtl_basepath) {}
|
||||
virtual ~MaterialFileReader() {}
|
||||
virtual std::string operator()(const std::string &matId,
|
||||
std::vector<material_t> &materials,
|
||||
std::map<std::string, int> &matMap);
|
||||
|
||||
private:
|
||||
std::string m_mtlBasePath;
|
||||
};
|
||||
|
||||
/// Loads .obj from a file.
|
||||
/// 'shapes' will be filled with parsed shape data
|
||||
/// The function returns error string.
|
||||
/// Returns empty string when loading .obj success.
|
||||
/// 'mtl_basepath' is optional, and used for base path for .mtl file.
|
||||
std::string LoadObj(std::vector<shape_t> &shapes, // [output]
|
||||
std::vector<material_t> &materials, // [output]
|
||||
const char *filename, const char *mtl_basepath = NULL);
|
||||
|
||||
/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve
|
||||
/// std::istream for materials.
|
||||
/// Returns empty string when loading .obj success.
|
||||
std::string LoadObj(std::vector<shape_t> &shapes, // [output]
|
||||
std::vector<material_t> &materials, // [output]
|
||||
std::istream &inStream, MaterialReader &readMatFn);
|
||||
|
||||
/// Loads materials into std::map
|
||||
/// Returns an empty string if successful
|
||||
std::string LoadMtl(std::map<std::string, int> &material_map,
|
||||
std::vector<material_t> &materials, std::istream &inStream);
|
||||
}
|
||||
|
||||
#endif // _TINY_OBJ_LOADER_H
|
||||
Submodule openspace-data updated: 1a280ed06a...05740b37f8
@@ -55,6 +55,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
|
||||
, _programObject(nullptr)
|
||||
, _texture(nullptr)
|
||||
, _geometry(nullptr)
|
||||
, _performShading("performShading", "Perform Shading", true)
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
@@ -150,6 +151,9 @@ void RenderableModel::render(const RenderData& data)
|
||||
_programObject->setUniform("ModelTransform", transform);
|
||||
setPscUniforms(_programObject, &data.camera, data.position);
|
||||
|
||||
_programObject->setUniform("_performShading", _performShading);
|
||||
|
||||
|
||||
// Bind texture
|
||||
ghoul::opengl::TextureUnit unit;
|
||||
unit.activate();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "WavefrontGeometry";
|
||||
}
|
||||
@@ -42,6 +43,7 @@ WavefrontGeometry::WavefrontGeometry(const ghoul::Dictionary& dictionary)
|
||||
, _vsize(0)
|
||||
, _varray(nullptr)
|
||||
, _iarray(nullptr)
|
||||
, _tarray(nullptr)
|
||||
{
|
||||
using constants::scenegraphnode::keyName;
|
||||
|
||||
@@ -70,161 +72,102 @@ WavefrontGeometry::WavefrontGeometry(const ghoul::Dictionary& dictionary)
|
||||
|
||||
void WavefrontGeometry::loadObj(const char *filename){
|
||||
// temporary
|
||||
int vertexSize = 0;
|
||||
int vertexNormalSize = 0;
|
||||
int vertexTextureSize = 0;
|
||||
int indicesSize = 0;
|
||||
const char *mtl_basepat = filename;
|
||||
|
||||
float f1, f2, f3;
|
||||
int i1, i2, i3, i4, i5, i6, i7, i8, i9;
|
||||
char line[150];
|
||||
//float maxtex = 0.0;
|
||||
std::string err = tinyobj::LoadObj(shapes, materials, filename, mtl_basepat);
|
||||
|
||||
_isize = shapes[0].mesh.indices.size();
|
||||
_vsize = shapes[0].mesh.indices.size(); // shapes[0].mesh.positions.size() + shapes[0].mesh.positions.size() / 3;
|
||||
_tsize = shapes[0].mesh.texcoords.size();
|
||||
|
||||
FILE *fi;
|
||||
// START LINE COUNT
|
||||
fi = fopen(filename, "r");
|
||||
if (fi == NULL) {
|
||||
LERROR("Null Object\n");
|
||||
|
||||
}
|
||||
while (fgets(line, 150, fi) != NULL)
|
||||
{
|
||||
if (sscanf(line, "v %f%f%f", &f1, &f2, &f3)) {
|
||||
vertexSize += 4;
|
||||
}
|
||||
if (sscanf(line, "vn %f%f%f", &f1, &f2, &f3)) {
|
||||
vertexNormalSize += 3;
|
||||
}
|
||||
if (sscanf(line, "vt %f%f%f", &f1, &f2, &f3)) {
|
||||
vertexTextureSize += 3;
|
||||
}
|
||||
if (vertexTextureSize > 0) {
|
||||
if (sscanf(line, "f %i/%i/%i %i/%i/%i %i/%i/%i", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9)) {
|
||||
indicesSize += 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sscanf(line, "f %i//%i %i//%i %i//%i", &i1, &i2, &i3, &i4, &i5, &i6)) {
|
||||
indicesSize += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
// allocate memory for all arrays
|
||||
_isize = indicesSize;
|
||||
_vsize = indicesSize;
|
||||
|
||||
// float arrays
|
||||
float* tempVertexArray = new float[vertexSize];
|
||||
float* tempVertexNormalArray = new float[vertexNormalSize];
|
||||
float* tempVertexTextureArray = new float[vertexTextureSize];
|
||||
_varray = new Vertex[_vsize];
|
||||
|
||||
// int arrays
|
||||
_iarray = new int[_isize];
|
||||
int *tempNormalIndicesArray = new int[_isize];
|
||||
int *tempTextureIndicesArray = new int[_isize];
|
||||
|
||||
// keeping track of the array indexes
|
||||
unsigned int i = 0;
|
||||
unsigned int n = 0;
|
||||
unsigned int m = 0;
|
||||
unsigned int w = 0;
|
||||
//copy indices
|
||||
for (int f = 0; f < shapes[0].mesh.indices.size(); f++) {
|
||||
_iarray[f] = shapes[0].mesh.indices[f];
|
||||
}
|
||||
|
||||
// Go back to beginning of file
|
||||
fseek(fi, 0, SEEK_SET);
|
||||
while (fgets(line, 150, fi) != NULL){
|
||||
if (sscanf(line, "v %f%f%f", &f1, &f2, &f3)){
|
||||
(tempVertexArray)[i] = f1;
|
||||
i++;
|
||||
(tempVertexArray)[i] = f2;
|
||||
i++;
|
||||
(tempVertexArray)[i] = f3;
|
||||
i++;
|
||||
}
|
||||
if (sscanf(line, "vn %f%f%f", &f1, &f2, &f3)){
|
||||
(tempVertexNormalArray)[n] = f1;
|
||||
n++;
|
||||
(tempVertexNormalArray)[n] = f2;
|
||||
n++;
|
||||
(tempVertexNormalArray)[n] = f3;
|
||||
n++;
|
||||
}
|
||||
if (sscanf(line, "vt %f%f%f", &f1, &f2, &f3)){
|
||||
(tempVertexTextureArray)[w] = f1;
|
||||
w++;
|
||||
(tempVertexTextureArray)[w] = f2;
|
||||
w++;
|
||||
(tempVertexTextureArray)[w] = f3;
|
||||
w++;
|
||||
}
|
||||
if (vertexTextureSize > 0){
|
||||
if (sscanf(line, "f %i/%i/%i %i/%i/%i %i/%i/%i", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9)){
|
||||
(_iarray)[m] = i1 - 1;
|
||||
(tempTextureIndicesArray)[m] = i2 - 1;
|
||||
(tempNormalIndicesArray)[m] = i3 - 1;
|
||||
m++;
|
||||
(_iarray)[m] = i4 - 1;
|
||||
(tempTextureIndicesArray)[m] = i5 - 1;
|
||||
(tempNormalIndicesArray)[m] = i6 - 1;
|
||||
m++;
|
||||
(_iarray)[m] = i7 - 1;
|
||||
(tempTextureIndicesArray)[m] = i8 - 1;
|
||||
(tempNormalIndicesArray)[m] = i9 - 1;
|
||||
m++;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (sscanf(line, "f %i//%i %i//%i %i//%i", &i1, &i2, &i3, &i4, &i5, &i6)){
|
||||
(_iarray)[m] = i1 - 1;
|
||||
(tempNormalIndicesArray)[m] = i2 - 1;
|
||||
m++;
|
||||
(_iarray)[m] = i3 - 1;
|
||||
(tempNormalIndicesArray)[m] = i4 - 1;
|
||||
m++;
|
||||
(_iarray)[m] = i5 - 1;
|
||||
(tempNormalIndicesArray)[m] = i6 - 1;
|
||||
m++;
|
||||
//shapes[0].mesh.texcoords.resize(2 * _isize);
|
||||
int p = 0;
|
||||
for (auto v : shapes[0].mesh.indices) {
|
||||
_varray[p].location[0] = shapes[0].mesh.positions[3 * v + 0];
|
||||
_varray[p].location[1] = shapes[0].mesh.positions[3 * v + 1];
|
||||
_varray[p].location[2] = shapes[0].mesh.positions[3 * v + 2];
|
||||
_varray[p].location[3] = 5;
|
||||
|
||||
_varray[p].normal[0] = shapes[0].mesh.normals[3 * v + 0];
|
||||
_varray[p].normal[1] = shapes[0].mesh.normals[3 * v + 1];
|
||||
_varray[p].normal[2] = shapes[0].mesh.normals[3 * v + 2];
|
||||
|
||||
p++;
|
||||
}
|
||||
p = 0;
|
||||
|
||||
//TEXCOORDS NOT MAPPING PROPERLY? WHY?
|
||||
/*
|
||||
if (shapes[0].mesh.texcoords.size() > 0) {
|
||||
for (size_t k = 0; k < shapes[0].mesh.indices.size() / 3; k++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int idx = shapes[0].mesh.indices[3 * k + j];
|
||||
|
||||
_varray[p].tex[0] = shapes[0].mesh.texcoords[2 * idx + 0];
|
||||
_varray[p].tex[1] = shapes[0].mesh.texcoords[2 * idx + 1];
|
||||
|
||||
std::cout << shapes[0].mesh.texcoords[2 * idx + 0] << " "
|
||||
<< shapes[0].mesh.texcoords[2 * idx + 1] << std::endl;
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fi);
|
||||
*/
|
||||
|
||||
// end of file read
|
||||
// creating the vertex array
|
||||
i = 0; n = 0; m = 0;
|
||||
int normalIndex = 0;
|
||||
int textureIndex = 0;
|
||||
int vertexIndex = 0;
|
||||
while (m < _vsize){
|
||||
normalIndex = tempNormalIndicesArray[m] * 3;
|
||||
textureIndex = tempTextureIndicesArray[m] * 3;
|
||||
vertexIndex = _iarray[m] * 3;
|
||||
_iarray[m] = m;
|
||||
//testing with one triangle - works.
|
||||
/*
|
||||
_varray[0].location[0] = 0;
|
||||
_varray[0].location[1] = 0;
|
||||
_varray[0].location[2] = 0;
|
||||
_varray[0].location[3] = 5;
|
||||
|
||||
_varray[m].location[3] = 6; // I need to set this proper at some point.
|
||||
int q = 0;
|
||||
while (q < 3){
|
||||
_varray[m].location[q] = tempVertexArray[vertexIndex + q];
|
||||
_varray[m].normal[q] = tempVertexNormalArray[normalIndex + q];
|
||||
q++;
|
||||
}
|
||||
_varray[0].normal[0] = 1;
|
||||
_varray[0].normal[1] = 1;
|
||||
_varray[0].normal[2] = 1;
|
||||
|
||||
if (vertexTextureSize > 0){
|
||||
_varray[m].tex[0] = tempVertexTextureArray[textureIndex];
|
||||
_varray[m].tex[1] = tempVertexTextureArray[textureIndex + 1];
|
||||
}
|
||||
else{
|
||||
_varray[m].tex[0] = 1.0;
|
||||
_varray[m].tex[1] = 1.0;
|
||||
}
|
||||
m++;
|
||||
}
|
||||
// free up memory
|
||||
delete[] tempVertexArray;
|
||||
delete[] tempVertexNormalArray;
|
||||
delete[] tempNormalIndicesArray;
|
||||
delete[] tempVertexTextureArray;
|
||||
delete[] tempTextureIndicesArray;
|
||||
_varray[0].tex[0] = 0;
|
||||
_varray[0].tex[1] = 0;
|
||||
|
||||
_varray[1].location[0] = 1;
|
||||
_varray[1].location[1] = 0;
|
||||
_varray[1].location[2] = 0;
|
||||
_varray[1].location[3] = 5;
|
||||
|
||||
_varray[1].normal[0] = 1;
|
||||
_varray[1].normal[1] = 1;
|
||||
_varray[1].normal[2] = 1;
|
||||
|
||||
_varray[1].tex[0] = 1;
|
||||
_varray[1].tex[1] = 0;
|
||||
|
||||
_varray[2].location[0] = 0;
|
||||
_varray[2].location[1] = 1;
|
||||
_varray[2].location[2] = 0;
|
||||
_varray[2].location[3] = 5;
|
||||
|
||||
_varray[2].normal[0] = 1;
|
||||
_varray[2].normal[1] = 1;
|
||||
_varray[2].normal[2] = 1;
|
||||
|
||||
_varray[2].tex[0] = 0;
|
||||
_varray[2].tex[1] = 1;
|
||||
|
||||
_vsize = 3;
|
||||
_isize = 3;
|
||||
|
||||
_iarray[0] = 0;
|
||||
_iarray[1] = 1;
|
||||
_iarray[2] = 2;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -250,7 +193,7 @@ bool WavefrontGeometry::initialize(RenderableModel* parent){
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<const GLvoid*>(offsetof(Vertex, location)));
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
|
||||
reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<const GLvoid*>(offsetof(Vertex, normal)));
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ bool ImageSequencer::parsePlaybookFile(const std::string& fileName, std::string
|
||||
if (size_t position = fileName.find_last_of(".") + 1){
|
||||
if (position != std::string::npos){
|
||||
std::string extension = ghoul::filesystem::File(fileName).fileExtension();
|
||||
|
||||
/*
|
||||
if (extension == "csv"){ // comma separated playbook
|
||||
std::cout << "USING COMMA SEPARATED TIMELINE V9F" << std::endl;
|
||||
|
||||
@@ -240,7 +240,7 @@ bool ImageSequencer::parsePlaybookFile(const std::string& fileName, std::string
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
if (extension == "txt"){// Hong Kang. pre-parsed playbook
|
||||
std::cout << "USING PREPARSED PLAYBOOK V9H" << std::endl;
|
||||
|
||||
698
src/util/tiny_obj_loader.cpp
Normal file
698
src/util/tiny_obj_loader.cpp
Normal file
@@ -0,0 +1,698 @@
|
||||
//
|
||||
// Copyright 2012-2015, Syoyo Fujita.
|
||||
//
|
||||
// Licensed under 2-clause BSD liecense.
|
||||
//
|
||||
|
||||
//
|
||||
// version 0.9.8: Fix multi-materials(per-face material ID).
|
||||
// version 0.9.7: Support multi-materials(per-face material ID) per
|
||||
// object/group.
|
||||
// version 0.9.6: Support Ni(index of refraction) mtl parameter.
|
||||
// Parse transmittance material parameter correctly.
|
||||
// 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>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <openspace/util/tiny_obj_loader.h>
|
||||
|
||||
namespace tinyobj {
|
||||
|
||||
struct vertex_index {
|
||||
int v_idx, vt_idx, vn_idx;
|
||||
vertex_index(){};
|
||||
vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx){};
|
||||
vertex_index(int vidx, int vtidx, int vnidx)
|
||||
: v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx){};
|
||||
};
|
||||
// for std::map
|
||||
static inline bool operator<(const vertex_index &a, const vertex_index &b) {
|
||||
if (a.v_idx != b.v_idx)
|
||||
return (a.v_idx < b.v_idx);
|
||||
if (a.vn_idx != b.vn_idx)
|
||||
return (a.vn_idx < b.vn_idx);
|
||||
if (a.vt_idx != b.vt_idx)
|
||||
return (a.vt_idx < b.vt_idx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct obj_shape {
|
||||
std::vector<float> v;
|
||||
std::vector<float> vn;
|
||||
std::vector<float> vt;
|
||||
};
|
||||
|
||||
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 int fixIndex(int idx, int n) {
|
||||
if (idx > 0) return idx - 1;
|
||||
if (idx == 0) return 0;
|
||||
return n + idx; // negative value = relative
|
||||
}
|
||||
|
||||
static inline std::string parseString(const char *&token) {
|
||||
std::string s;
|
||||
token += strspn(token, " \t");
|
||||
int e = strcspn(token, " \t\r");
|
||||
s = std::string(token, &token[e]);
|
||||
token += e;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline int parseInt(const char *&token) {
|
||||
token += strspn(token, " \t");
|
||||
int i = atoi(token);
|
||||
token += strcspn(token, " \t\r");
|
||||
return i;
|
||||
}
|
||||
|
||||
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: i, i/j/k, i//k, i/j
|
||||
static vertex_index parseTriple(const char *&token, int vsize, int vnsize,
|
||||
int vtsize) {
|
||||
vertex_index vi(-1);
|
||||
|
||||
vi.v_idx = fixIndex(atoi(token), vsize);
|
||||
token += strcspn(token, "/ \t\r");
|
||||
if (token[0] != '/') {
|
||||
return vi;
|
||||
}
|
||||
token++;
|
||||
|
||||
// i//k
|
||||
if (token[0] == '/') {
|
||||
token++;
|
||||
vi.vn_idx = fixIndex(atoi(token), vnsize);
|
||||
token += strcspn(token, "/ \t\r");
|
||||
return vi;
|
||||
}
|
||||
|
||||
// i/j/k or i/j
|
||||
vi.vt_idx = fixIndex(atoi(token), vtsize);
|
||||
token += strcspn(token, "/ \t\r");
|
||||
if (token[0] != '/') {
|
||||
return vi;
|
||||
}
|
||||
|
||||
// i/j/k
|
||||
token++; // skip '/'
|
||||
vi.vn_idx = fixIndex(atoi(token), vnsize);
|
||||
token += strcspn(token, "/ \t\r");
|
||||
return vi;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
updateVertex(std::map<vertex_index, unsigned int> &vertexCache,
|
||||
std::vector<float> &positions, std::vector<float> &normals,
|
||||
std::vector<float> &texcoords,
|
||||
const std::vector<float> &in_positions,
|
||||
const std::vector<float> &in_normals,
|
||||
const std::vector<float> &in_texcoords, const vertex_index &i) {
|
||||
const std::map<vertex_index, unsigned int>::iterator it = vertexCache.find(i);
|
||||
|
||||
if (it != vertexCache.end()) {
|
||||
// found cache
|
||||
return it->second;
|
||||
}
|
||||
|
||||
assert(in_positions.size() > (unsigned int)(3 * i.v_idx + 2));
|
||||
|
||||
positions.push_back(in_positions[3 * i.v_idx + 0]);
|
||||
positions.push_back(in_positions[3 * i.v_idx + 1]);
|
||||
positions.push_back(in_positions[3 * i.v_idx + 2]);
|
||||
|
||||
if (i.vn_idx >= 0) {
|
||||
normals.push_back(in_normals[3 * i.vn_idx + 0]);
|
||||
normals.push_back(in_normals[3 * i.vn_idx + 1]);
|
||||
normals.push_back(in_normals[3 * i.vn_idx + 2]);
|
||||
}
|
||||
|
||||
if (i.vt_idx >= 0) {
|
||||
texcoords.push_back(in_texcoords[2 * i.vt_idx + 0]);
|
||||
texcoords.push_back(in_texcoords[2 * i.vt_idx + 1]);
|
||||
}
|
||||
|
||||
unsigned int idx = positions.size() / 3 - 1;
|
||||
vertexCache[i] = idx;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void InitMaterial(material_t &material) {
|
||||
material.name = "";
|
||||
material.ambient_texname = "";
|
||||
material.diffuse_texname = "";
|
||||
material.specular_texname = "";
|
||||
material.normal_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.illum = 0;
|
||||
material.dissolve = 1.f;
|
||||
material.shininess = 1.f;
|
||||
material.ior = 1.f;
|
||||
material.unknown_parameter.clear();
|
||||
}
|
||||
|
||||
static bool exportFaceGroupToShape(
|
||||
shape_t &shape, std::map<vertex_index, unsigned int> vertexCache,
|
||||
const std::vector<float> &in_positions,
|
||||
const std::vector<float> &in_normals,
|
||||
const std::vector<float> &in_texcoords,
|
||||
const std::vector<std::vector<vertex_index> > &faceGroup,
|
||||
const int material_id, const std::string &name, bool clearCache) {
|
||||
if (faceGroup.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flatten vertices and indices
|
||||
for (size_t i = 0; i < faceGroup.size(); i++) {
|
||||
const std::vector<vertex_index> &face = faceGroup[i];
|
||||
|
||||
vertex_index i0 = face[0];
|
||||
vertex_index i1(-1);
|
||||
vertex_index i2 = face[1];
|
||||
|
||||
size_t npolys = face.size();
|
||||
|
||||
// Polygon -> triangle fan conversion
|
||||
for (size_t k = 2; k < npolys; k++) {
|
||||
i1 = i2;
|
||||
i2 = face[k];
|
||||
|
||||
unsigned int v0 = updateVertex(
|
||||
vertexCache, shape.mesh.positions, shape.mesh.normals,
|
||||
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i0);
|
||||
unsigned int v1 = updateVertex(
|
||||
vertexCache, shape.mesh.positions, shape.mesh.normals,
|
||||
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i1);
|
||||
unsigned int v2 = updateVertex(
|
||||
vertexCache, shape.mesh.positions, shape.mesh.normals,
|
||||
shape.mesh.texcoords, in_positions, in_normals, in_texcoords, i2);
|
||||
|
||||
shape.mesh.indices.push_back(v0);
|
||||
shape.mesh.indices.push_back(v1);
|
||||
shape.mesh.indices.push_back(v2);
|
||||
|
||||
shape.mesh.material_ids.push_back(material_id);
|
||||
}
|
||||
}
|
||||
|
||||
shape.name = name;
|
||||
|
||||
if (clearCache)
|
||||
vertexCache.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LoadMtl(std::map<std::string, int> &material_map,
|
||||
std::vector<material_t> &materials,
|
||||
std::istream &inStream) {
|
||||
material_map.clear();
|
||||
std::stringstream err;
|
||||
|
||||
material_t material;
|
||||
|
||||
int maxchars = 8192; // Alloc enough size.
|
||||
std::vector<char> buf(maxchars); // Alloc enough size.
|
||||
while (inStream.peek() != -1) {
|
||||
inStream.getline(&buf[0], maxchars);
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
|
||||
// Trim newline '\r\n' or '\n'
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (!material.name.empty()) {
|
||||
material_map.insert(
|
||||
std::pair<std::string, int>(material.name, materials.size()));
|
||||
materials.push_back(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;
|
||||
}
|
||||
|
||||
// transmittance
|
||||
if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) {
|
||||
token += 2;
|
||||
float r, g, b;
|
||||
parseFloat3(r, g, b, token);
|
||||
material.transmittance[0] = r;
|
||||
material.transmittance[1] = g;
|
||||
material.transmittance[2] = b;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ior(index of refraction)
|
||||
if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) {
|
||||
token += 2;
|
||||
material.ior = parseFloat(token);
|
||||
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;
|
||||
}
|
||||
|
||||
// illum model
|
||||
if (0 == strncmp(token, "illum", 5) && isSpace(token[5])) {
|
||||
token += 6;
|
||||
material.illum = parseInt(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// dissolve
|
||||
if ((token[0] == 'd' && isSpace(token[1]))) {
|
||||
token += 1;
|
||||
material.dissolve = parseFloat(token);
|
||||
continue;
|
||||
}
|
||||
if (token[0] == 'T' && token[1] == 'r' && isSpace(token[2])) {
|
||||
token += 2;
|
||||
material.dissolve = 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));
|
||||
}
|
||||
}
|
||||
// flush last material.
|
||||
material_map.insert(
|
||||
std::pair<std::string, int>(material.name, materials.size()));
|
||||
materials.push_back(material);
|
||||
|
||||
return err.str();
|
||||
}
|
||||
|
||||
std::string MaterialFileReader::operator()(const std::string &matId,
|
||||
std::vector<material_t> &materials,
|
||||
std::map<std::string, int> &matMap) {
|
||||
std::string filepath;
|
||||
|
||||
if (!m_mtlBasePath.empty()) {
|
||||
filepath = std::string(m_mtlBasePath) + matId;
|
||||
} else {
|
||||
filepath = matId;
|
||||
}
|
||||
|
||||
std::ifstream matIStream(filepath.c_str());
|
||||
return LoadMtl(matMap, materials, matIStream);
|
||||
}
|
||||
|
||||
std::string LoadObj(std::vector<shape_t> &shapes,
|
||||
std::vector<material_t> &materials, // [output]
|
||||
const char *filename, const char *mtl_basepath) {
|
||||
|
||||
shapes.clear();
|
||||
|
||||
std::stringstream err;
|
||||
|
||||
std::ifstream ifs(filename);
|
||||
if (!ifs) {
|
||||
err << "Cannot open file [" << filename << "]" << std::endl;
|
||||
return err.str();
|
||||
}
|
||||
|
||||
std::string basePath;
|
||||
if (mtl_basepath) {
|
||||
basePath = mtl_basepath;
|
||||
}
|
||||
MaterialFileReader matFileReader(basePath);
|
||||
|
||||
return LoadObj(shapes, materials, ifs, matFileReader);
|
||||
}
|
||||
|
||||
std::string LoadObj(std::vector<shape_t> &shapes,
|
||||
std::vector<material_t> &materials, // [output]
|
||||
std::istream &inStream, MaterialReader &readMatFn) {
|
||||
std::stringstream err;
|
||||
|
||||
std::vector<float> v;
|
||||
std::vector<float> vn;
|
||||
std::vector<float> vt;
|
||||
std::vector<std::vector<vertex_index> > faceGroup;
|
||||
std::string name;
|
||||
|
||||
// material
|
||||
std::map<std::string, int> material_map;
|
||||
std::map<vertex_index, unsigned int> vertexCache;
|
||||
int material = -1;
|
||||
|
||||
shape_t shape;
|
||||
|
||||
int maxchars = 8192; // Alloc enough size.
|
||||
std::vector<char> buf(maxchars); // Alloc enough size.
|
||||
while (inStream.peek() != -1) {
|
||||
inStream.getline(&buf[0], maxchars);
|
||||
|
||||
std::string linebuf(&buf[0]);
|
||||
|
||||
// Trim newline '\r\n' or '\n'
|
||||
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;
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
std::vector<vertex_index> face;
|
||||
while (!isNewLine(token[0])) {
|
||||
vertex_index vi =
|
||||
parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2);
|
||||
face.push_back(vi);
|
||||
int 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);
|
||||
|
||||
// Create face group per material.
|
||||
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
|
||||
faceGroup, material, name, true);
|
||||
if (ret) {
|
||||
faceGroup.clear();
|
||||
}
|
||||
|
||||
if (material_map.find(namebuf) != material_map.end()) {
|
||||
material = material_map[namebuf];
|
||||
} else {
|
||||
// { error!! material not found }
|
||||
material = -1;
|
||||
}
|
||||
|
||||
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 = readMatFn(namebuf, materials, material_map);
|
||||
if (!err_mtl.empty()) {
|
||||
faceGroup.clear(); // for safety
|
||||
return err_mtl;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// group name
|
||||
if (token[0] == 'g' && isSpace((token[1]))) {
|
||||
|
||||
// flush previous face group.
|
||||
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
|
||||
faceGroup, material, name, true);
|
||||
if (ret) {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
|
||||
shape = shape_t();
|
||||
|
||||
// material = -1;
|
||||
faceGroup.clear();
|
||||
|
||||
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.
|
||||
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt,
|
||||
faceGroup, material, name, true);
|
||||
if (ret) {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
|
||||
// material = -1;
|
||||
faceGroup.clear();
|
||||
shape = shape_t();
|
||||
|
||||
// @todo { multiple object name? }
|
||||
char namebuf[4096];
|
||||
token += 2;
|
||||
sscanf(token, "%s", namebuf);
|
||||
name = std::string(namebuf);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore unknown command.
|
||||
}
|
||||
|
||||
bool ret = exportFaceGroupToShape(shape, vertexCache, v, vn, vt, faceGroup,
|
||||
material, name, true);
|
||||
if (ret) {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
faceGroup.clear(); // for safety
|
||||
|
||||
return err.str();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user