1184 lines
33 KiB
C++
1184 lines
33 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004 \/)\/ *
|
|
* Visual Computing Lab /\/| *
|
|
* ISTI - Italian National Research Council | *
|
|
* \ *
|
|
* All rights reserved. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
|
* for more details. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
History
|
|
|
|
$Log: not supported by cvs2svn $
|
|
Revision 1.4 2006/03/29 09:27:07 cignoni
|
|
Added managemnt of non critical errors
|
|
|
|
Revision 1.3 2006/03/29 08:51:16 corsini
|
|
reset to zero warnings
|
|
|
|
Revision 1.2 2006/03/27 07:18:22 cignoni
|
|
added missing std::
|
|
|
|
Revision 1.1 2006/03/07 13:19:29 cignoni
|
|
First Release with OBJ import support
|
|
|
|
Initial Working version coded by Buzzelli.
|
|
****************************************************************************/
|
|
|
|
#ifndef __VCGLIB_IMPORT_OBJ
|
|
#define __VCGLIB_IMPORT_OBJ
|
|
|
|
#include <wrap/callback.h>
|
|
#include <vcg/complex/trimesh/allocate.h>
|
|
#include <wrap/io_trimesh/io_mask.h>
|
|
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
namespace vcg {
|
|
namespace tri {
|
|
namespace io {
|
|
|
|
/**
|
|
This class encapsulate a filter for importing obj (Alias Wavefront) meshes.
|
|
Warning: this code assume little endian (PC) architecture!!!
|
|
*/
|
|
template <class OpenMeshType>
|
|
class ImporterOBJ
|
|
{
|
|
public:
|
|
|
|
typedef typename OpenMeshType::VertexPointer VertexPointer;
|
|
typedef typename OpenMeshType::ScalarType ScalarType;
|
|
typedef typename OpenMeshType::VertexType VertexType;
|
|
typedef typename OpenMeshType::FaceType FaceType;
|
|
typedef typename OpenMeshType::VertexIterator VertexIterator;
|
|
typedef typename OpenMeshType::FaceIterator FaceIterator;
|
|
typedef typename OpenMeshType::CoordType CoordType;
|
|
|
|
class Info
|
|
{
|
|
public:
|
|
|
|
Info()
|
|
{
|
|
mask = 0;
|
|
cb = 0;
|
|
}
|
|
|
|
/// It returns a bit mask describing the field preesnt in the ply file
|
|
int mask;
|
|
|
|
/// a Simple callback that can be used for long obj parsing.
|
|
// it returns the current position, and formats a string with a description of what th efunction is doing (loading vertexes, faces...)
|
|
CallBackPos *cb;
|
|
|
|
/// number of vertices
|
|
int numVertices;
|
|
|
|
/// number of triangles
|
|
int numTriangles;
|
|
|
|
}; // end class
|
|
|
|
|
|
struct OBJFacet
|
|
{
|
|
CoordType n;
|
|
CoordType t;
|
|
CoordType v[3];
|
|
|
|
short attr; // material index
|
|
};
|
|
|
|
struct TexCoord
|
|
{
|
|
float u;
|
|
float v;
|
|
};
|
|
|
|
struct Material
|
|
{
|
|
Material()
|
|
{
|
|
strcpy(name, "default_material");
|
|
ambient = Point3f( .2f, .2f, .2f);
|
|
diffuse = Point3f(1.0f, 1.0f, 1.0f);
|
|
specular = Point3f(1.0f, 1.0f, 1.0f);
|
|
|
|
shininess = 0;
|
|
alpha = 1.0f;
|
|
|
|
strcpy(textureFileName, "");
|
|
textureIdx = -1;
|
|
};
|
|
|
|
char name[FILENAME_MAX];
|
|
|
|
Point3f ambient;
|
|
Point3f diffuse;
|
|
Point3f specular;
|
|
|
|
int shininess;
|
|
float alpha;
|
|
|
|
bool bSpecular;
|
|
|
|
char textureFileName[FILENAME_MAX];
|
|
short textureIdx;
|
|
};
|
|
|
|
enum OBJError {
|
|
// Successfull opening
|
|
E_NOERROR = 0x000, // 0 (position of correspondig string in the array)
|
|
|
|
// Non Critical Errors (only odd numbers)
|
|
E_NON_CRITICAL_ERROR = 0x001,
|
|
E_MATERIAL_FILE_NOT_FOUND = 0x003, // 1
|
|
E_MATERIAL_NOT_FOUND = 0x005, // 2
|
|
E_TEXTURE_NOT_FOUND = 0x007, // 3
|
|
E_VERTICES_WITH_SAME_IDX_IN_FACE = 0x009, // 4
|
|
|
|
// Critical Opening Errors (only even numbers)
|
|
E_CANTOPEN = 0x00A, // 5
|
|
E_UNESPECTEDEOF = 0x00C, // 6
|
|
E_ABORTED = 0x00E, // 7
|
|
E_NO_VERTEX = 0x010, // 8
|
|
E_NO_FACE = 0x012, // 9
|
|
E_BAD_VERTEX_STATEMENT = 0x014, // 10
|
|
E_BAD_VERT_TEX_STATEMENT = 0x016, // 11
|
|
E_BAD_VERT_NORMAL_STATEMENT = 0x018, // 12
|
|
E_LESS_THAN_3VERTINFACE = 0x01A, // 13
|
|
E_BAD_VERT_INDEX = 0x01C, // 14
|
|
E_BAD_VERT_TEX_INDEX = 0x01E, // 15
|
|
E_BAD_VERT_NORMAL_INDEX = 0x020 // 16
|
|
};
|
|
|
|
// to check if a given error is critical or not.
|
|
static bool ErrorCritical(int err)
|
|
{
|
|
if(err<0x00A && err>=0) return false;
|
|
return true;
|
|
}
|
|
|
|
static const char* ErrorMsg(int error)
|
|
{
|
|
static const char* obj_error_msg[] =
|
|
{
|
|
"No errors", // 0
|
|
|
|
"Material library file not found, a default white material is used", // 1
|
|
"Some materials definitions were not found, a default white material is used where no material was available", // 2
|
|
"Texture file not found", // 3
|
|
"Identical index vertices found in the same face", // 4
|
|
|
|
"Can't open file", // 5
|
|
"Premature End of file", // 6
|
|
"File opening aborted", // 7
|
|
"No vertex field found", // 8
|
|
"No face field found", // 9
|
|
"Vertex statement with less than 3 coords", // 10
|
|
"Texture coords statement with less than 2 coords", // 11
|
|
"Vertex normal statement with less than 3 coords", // 12
|
|
"Face with less than 3 vertices", // 13
|
|
"Bad vertex index in face", // 14
|
|
"Bad texture coords index in face", // 15
|
|
"Bad vertex normal index in face" // 16
|
|
};
|
|
|
|
// due to approximation, following line works well for either even (critical err codes)
|
|
// or odd (non critical ones) numbers
|
|
error = (int) error/2;
|
|
|
|
if(error>15 || error<0) return "Unknown error";
|
|
else return obj_error_msg[error];
|
|
};
|
|
|
|
static int Open(OpenMeshType &mesh, const char *filename, int &loadmask, CallBackPos *cb=0)
|
|
{
|
|
Info oi;
|
|
oi.mask=-1;
|
|
oi.cb=cb;
|
|
int ret=Open(mesh,filename,oi);
|
|
loadmask=oi.mask;
|
|
return ret;
|
|
}
|
|
|
|
/*!
|
|
* Opens an object file (in ascii format) and populates the mesh passed as first
|
|
* accordingly to read data
|
|
* \param m The mesh model to be populated with data stored into the file
|
|
* \param filename The name of the file to be opened
|
|
* \param oi A structure containing infos about the object to be opened
|
|
*/
|
|
static int Open( OpenMeshType &m, const char * filename, Info &oi)
|
|
{
|
|
int result = E_NOERROR;
|
|
|
|
m.Clear();
|
|
CallBackPos *cb = oi.cb;
|
|
|
|
// if LoadMask has not been called yet, we call it here
|
|
if (oi.mask == -1)
|
|
LoadMask(filename, oi);
|
|
|
|
if (oi.numVertices == 0)
|
|
return E_NO_VERTEX;
|
|
|
|
if (oi.numTriangles == 0)
|
|
return E_NO_FACE;
|
|
|
|
std::ifstream stream(filename);
|
|
if (stream.fail())
|
|
return E_CANTOPEN;
|
|
|
|
std::vector<Material> materials; // materials vector
|
|
std::vector<TexCoord> texCoords; // texture coordinates
|
|
std::vector<CoordType> normals; // vertex normals
|
|
|
|
std::vector< std::string > tokens;
|
|
std::string header;
|
|
|
|
short currentMaterialIdx = 0; // index of current material into materials vector
|
|
Material defaultMaterial; // default material: white
|
|
materials.push_back(defaultMaterial);
|
|
|
|
int numVertices = 0; // stores the number of vertices been read till now
|
|
int numTriangles = 0; // stores the number of faces been read till now
|
|
int numTexCoords = 0; // stores the number of texture coordinates been read till now
|
|
int numVNormals = 0; // stores the number of vertex normals been read till now
|
|
|
|
int numVerticesPlusFaces = oi.numVertices + oi.numTriangles;
|
|
|
|
// vertices and faces allocatetion
|
|
VertexIterator vi = Allocator<OpenMeshType>::AddVertices(m,oi.numVertices);
|
|
FaceIterator fi = Allocator<OpenMeshType>::AddFaces(m,oi.numTriangles);
|
|
|
|
// parsing file
|
|
while (!stream.eof())
|
|
{
|
|
tokens.clear();
|
|
TokenizeNextLine(stream, tokens);
|
|
|
|
unsigned int numTokens = static_cast<unsigned int>(tokens.size());
|
|
if (numTokens > 0)
|
|
{
|
|
header.clear();
|
|
header = tokens[0];
|
|
|
|
if (header.compare("v")==0) // vertex
|
|
{
|
|
if (numTokens < 4) return E_BAD_VERTEX_STATEMENT;
|
|
|
|
(*vi).P()[0] = (ScalarType) atof(tokens[1].c_str());
|
|
(*vi).P()[1] = (ScalarType) atof(tokens[2].c_str());
|
|
(*vi).P()[2] = (ScalarType) atof(tokens[3].c_str());
|
|
++numVertices;
|
|
|
|
// assigning vertex color
|
|
// ----------------------
|
|
if( oi.mask & vcg::tri::io::Mask::IOM_VERTCOLOR)
|
|
{
|
|
Material material = materials[currentMaterialIdx];
|
|
Point3f diffuseColor = material.diffuse;
|
|
unsigned char r = (unsigned char) (diffuseColor[0] * 255.0);
|
|
unsigned char g = (unsigned char) (diffuseColor[1] * 255.0);
|
|
unsigned char b = (unsigned char) (diffuseColor[2] * 255.0);
|
|
unsigned char alpha = (unsigned char) (material.alpha * 255.0);
|
|
Color4b vertexColor = Color4b(r, g, b, alpha);
|
|
(*vi).C()[0] = vertexColor[0];
|
|
(*vi).C()[1] = vertexColor[1];
|
|
(*vi).C()[2] = vertexColor[2];
|
|
(*vi).C()[3] = vertexColor[3];
|
|
}
|
|
|
|
++vi; // move to next vertex iterator
|
|
|
|
// callback invocation, abort loading process if the call returns false
|
|
if ((cb !=NULL) && (((numTriangles + numVertices)%100)==0) && !(*cb)((100*(numTriangles + numVertices))/numVerticesPlusFaces, "Vertex Loading"))
|
|
return E_ABORTED;
|
|
}
|
|
else if (header.compare("vt")==0) // vertex texture coords
|
|
{
|
|
if (numTokens < 3) return E_BAD_VERT_TEX_STATEMENT;
|
|
|
|
TexCoord t;
|
|
t.u = static_cast<float>(atof(tokens[1].c_str()));
|
|
t.v = static_cast<float>(atof(tokens[2].c_str()));
|
|
texCoords.push_back(t);
|
|
|
|
numTexCoords++;
|
|
}
|
|
else if (header.compare("vn")==0) // vertex normal
|
|
{
|
|
if (numTokens != 4) return E_BAD_VERT_NORMAL_STATEMENT;
|
|
|
|
CoordType n;
|
|
n[0] = (ScalarType) atof(tokens[1].c_str());
|
|
n[1] = (ScalarType) atof(tokens[2].c_str());
|
|
n[2] = (ScalarType) atof(tokens[3].c_str());
|
|
normals.push_back(n);
|
|
|
|
numVNormals++;
|
|
}
|
|
else if (header.compare("f")==0) // face
|
|
{
|
|
if (numTokens < 4) return E_LESS_THAN_3VERTINFACE;
|
|
|
|
int v1_index, v2_index, v3_index;
|
|
int vt1_index, vt2_index, vt3_index;
|
|
int vn1_index, vn2_index, vn3_index;
|
|
|
|
if (( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD ) &&
|
|
( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL ) )
|
|
{
|
|
std::string vertex;
|
|
std::string texcoord;
|
|
std::string normal;
|
|
|
|
SplitVVTVNToken(tokens[1], vertex, texcoord, normal);
|
|
v1_index = atoi(vertex.c_str());
|
|
vt1_index = atoi(texcoord.c_str());
|
|
vn1_index = atoi(normal.c_str());
|
|
|
|
SplitVVTVNToken(tokens[2], vertex, texcoord, normal);
|
|
v2_index = atoi(vertex.c_str());
|
|
vt2_index = atoi(texcoord.c_str());
|
|
vn2_index = atoi(normal.c_str());
|
|
|
|
SplitVVTVNToken(tokens[3], vertex, texcoord, normal);
|
|
v3_index = atoi(vertex.c_str());
|
|
vt3_index = atoi(texcoord.c_str());
|
|
vn3_index = atoi(normal.c_str());
|
|
}
|
|
else if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
|
|
{
|
|
std::string vertex;
|
|
std::string texcoord;
|
|
|
|
SplitVVTToken(tokens[1], vertex, texcoord);
|
|
v1_index = atoi(vertex.c_str());
|
|
vt1_index = atoi(texcoord.c_str());
|
|
|
|
SplitVVTToken(tokens[2], vertex, texcoord);
|
|
v2_index = atoi(vertex.c_str());
|
|
vt2_index = atoi(texcoord.c_str());
|
|
|
|
SplitVVTToken(tokens[3], vertex, texcoord);
|
|
v3_index = atoi(vertex.c_str());
|
|
vt3_index = atoi(texcoord.c_str());
|
|
}
|
|
else if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
|
|
{
|
|
std::string vertex;
|
|
std::string normal;
|
|
|
|
SplitVVNToken(tokens[1], vertex, normal);
|
|
v1_index = atoi(vertex.c_str());
|
|
vn1_index = atoi(normal.c_str());
|
|
|
|
SplitVVNToken(tokens[2], vertex, normal);
|
|
v2_index = atoi(vertex.c_str());
|
|
vn2_index = atoi(normal.c_str());
|
|
|
|
SplitVVNToken(tokens[3], vertex, normal);
|
|
v3_index = atoi(vertex.c_str());
|
|
vn3_index = atoi(normal.c_str());
|
|
}
|
|
else
|
|
{
|
|
v1_index = atoi(tokens[1].c_str());
|
|
v2_index = atoi(tokens[2].c_str());
|
|
v3_index = atoi(tokens[3].c_str());
|
|
}
|
|
|
|
if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
|
|
{
|
|
// verifying validity of texture coords indices
|
|
// --------------------------------------------
|
|
if (vt1_index < 0)
|
|
{
|
|
vt1_index += numTexCoords;
|
|
if (vt1_index < 0) return E_BAD_VERT_TEX_INDEX;
|
|
}
|
|
else if (vt1_index > numTexCoords) return E_BAD_VERT_TEX_INDEX;
|
|
else --vt1_index;
|
|
|
|
if (vt2_index < 0)
|
|
{
|
|
vt2_index += numTexCoords;
|
|
if (vt2_index < 0) return E_BAD_VERT_TEX_INDEX;
|
|
}
|
|
else if (vt2_index > numTexCoords) return E_BAD_VERT_TEX_INDEX;
|
|
else --vt2_index;
|
|
|
|
if (vt3_index < 0)
|
|
{
|
|
vt3_index += numTexCoords;
|
|
if (vt3_index < 0) return E_BAD_VERT_TEX_INDEX;
|
|
}
|
|
else if (vt3_index > numTexCoords) return E_BAD_VERT_TEX_INDEX;
|
|
else --vt3_index;
|
|
|
|
|
|
// assigning wedge texture coordinates
|
|
// -----------------------------------
|
|
Material material = materials[currentMaterialIdx];
|
|
|
|
TexCoord t = texCoords[vt1_index];
|
|
(*fi).WT(0).u() = t.u;
|
|
(*fi).WT(0).v() = t.v;
|
|
(*fi).WT(0).n() = material.textureIdx;
|
|
|
|
t = texCoords[vt2_index];
|
|
(*fi).WT(1).u() = t.u;
|
|
(*fi).WT(1).v() = t.v;
|
|
(*fi).WT(1).n() = material.textureIdx;
|
|
|
|
t = texCoords[vt3_index];
|
|
(*fi).WT(2).u() = t.u;
|
|
(*fi).WT(2).v() = t.v;
|
|
(*fi).WT(2).n() = material.textureIdx;
|
|
}
|
|
|
|
// verifying validity of vertex indices
|
|
// ------------------------------------
|
|
if (v1_index < 0)
|
|
{
|
|
v1_index += numVertices;
|
|
if (v1_index < 0) return E_BAD_VERT_INDEX;
|
|
}
|
|
else if (v1_index > numVertices) return E_BAD_VERT_INDEX;
|
|
else v1_index--; // since index starts from 1 instead of 0
|
|
|
|
if (v2_index < 0)
|
|
{
|
|
v2_index += numVertices;
|
|
if (v2_index < 0) return E_BAD_VERT_INDEX;
|
|
}
|
|
else if (v2_index > numVertices) return E_BAD_VERT_INDEX;
|
|
else v2_index--; // since index starts from 1 instead of 0
|
|
|
|
if (v3_index < 0)
|
|
{
|
|
v3_index += numVertices;
|
|
if (v3_index < 0) return E_BAD_VERT_INDEX;
|
|
}
|
|
else if (v3_index > numVertices) return E_BAD_VERT_INDEX;
|
|
else v3_index--; // since index starts from 1 instead of 0
|
|
|
|
if ((v1_index == v2_index) ||
|
|
(v1_index == v3_index) ||
|
|
(v2_index == v3_index))
|
|
result = E_VERTICES_WITH_SAME_IDX_IN_FACE;
|
|
|
|
// assigning face vertices
|
|
// -----------------------
|
|
(*fi).V(0) = &(m.vert[ v1_index ]);
|
|
(*fi).V(1) = &(m.vert[ v2_index ]);
|
|
(*fi).V(2) = &(m.vert[ v3_index ]);
|
|
|
|
// assigning face normal
|
|
// ---------------------
|
|
if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
|
|
{
|
|
// verifying validity of vertex normal indices
|
|
// -------------------------------------------
|
|
if (vn1_index < 0)
|
|
{
|
|
vn1_index += numVNormals;
|
|
if (vn1_index < 0) return E_BAD_VERT_NORMAL_INDEX;
|
|
}
|
|
else if (vn1_index > numVNormals) return E_BAD_VERT_NORMAL_INDEX;
|
|
else vn1_index--;
|
|
|
|
if (vn2_index < 0)
|
|
{
|
|
vn2_index += numVNormals;
|
|
if (vn2_index < 0) return E_BAD_VERT_NORMAL_INDEX;
|
|
}
|
|
else if (vn2_index > numVNormals) return E_BAD_VERT_NORMAL_INDEX;
|
|
else vn2_index--;
|
|
|
|
if (vn3_index < 0)
|
|
{
|
|
vn3_index += numVNormals;
|
|
if (vn3_index < 0) return E_BAD_VERT_NORMAL_INDEX;
|
|
}
|
|
else if (vn3_index > numVNormals) return E_BAD_VERT_NORMAL_INDEX;
|
|
else vn3_index--;
|
|
|
|
|
|
// face normal is computed as an average of wedge normals
|
|
CoordType n = (normals[vn1_index] + normals[vn2_index] + normals[vn3_index]);
|
|
n.Normalize();
|
|
|
|
(*fi).N() = n;
|
|
|
|
(*fi).WN(0) = normals[vn1_index];
|
|
(*fi).WN(1) = normals[vn2_index];
|
|
(*fi).WN(2) = normals[vn3_index];
|
|
}
|
|
else // computing face normal from position of face vertices
|
|
face::ComputeNormalizedNormal(*fi);
|
|
|
|
// assigning face color
|
|
// --------------------
|
|
Color4b faceColor; // we declare this outside code block since other
|
|
// triangles of this face will share the same color
|
|
if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR)
|
|
{
|
|
Material material = materials[currentMaterialIdx];
|
|
Point3f diffuseColor = material.diffuse;
|
|
unsigned char r = (unsigned char) (diffuseColor[0] * 255.0);
|
|
unsigned char g = (unsigned char) (diffuseColor[1] * 255.0);
|
|
unsigned char b = (unsigned char) (diffuseColor[2] * 255.0);
|
|
unsigned char alpha = (unsigned char) (material.alpha * 255.0);
|
|
faceColor = Color4b(r, g, b, alpha);
|
|
(*fi).C()[0] = faceColor[0];
|
|
(*fi).C()[1] = faceColor[1];
|
|
(*fi).C()[2] = faceColor[2];
|
|
(*fi).C()[3] = faceColor[3];
|
|
}
|
|
|
|
++fi;
|
|
++numTriangles;
|
|
|
|
int vertexesPerFace = static_cast<int>(tokens.size()-1);
|
|
int iVertex = 3;
|
|
while (iVertex < vertexesPerFace) // add other triangles
|
|
{
|
|
int v4_index;
|
|
int vt4_index;
|
|
int vn4_index;
|
|
|
|
if (( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD ) &&
|
|
( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL ) )
|
|
{
|
|
std::string vertex;
|
|
std::string texcoord;
|
|
std::string normal;
|
|
|
|
SplitVVTVNToken(tokens[++iVertex], vertex, texcoord, normal);
|
|
v4_index = atoi(vertex.c_str());
|
|
vt4_index = atoi(texcoord.c_str());
|
|
vn4_index = atoi(normal.c_str());
|
|
}
|
|
else if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
|
|
{
|
|
std::string vertex;
|
|
std::string texcoord;
|
|
|
|
SplitVVTToken(tokens[++iVertex], vertex, texcoord);
|
|
v4_index = atoi(vertex.c_str());
|
|
vt4_index = atoi(texcoord.c_str());
|
|
}
|
|
else if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
|
|
{
|
|
std::string vertex;
|
|
std::string normal;
|
|
|
|
SplitVVNToken(tokens[++iVertex], vertex, normal);
|
|
v4_index = atoi(vertex.c_str());
|
|
vn4_index = atoi(normal.c_str());
|
|
}
|
|
else
|
|
v4_index = atoi(tokens[++iVertex].c_str());
|
|
|
|
// assigning wedge texture coordinates
|
|
// -----------------------------------
|
|
if( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
|
|
{
|
|
// verifying validity of texture coords index
|
|
// ------------------------------------------
|
|
if (vt4_index < 0)
|
|
{
|
|
vt4_index += numTexCoords;
|
|
if (vt4_index < 0) return E_BAD_VERT_TEX_INDEX;
|
|
}
|
|
else if (vt4_index > numTexCoords) return E_BAD_VERT_TEX_INDEX;
|
|
else --vt4_index;
|
|
|
|
|
|
Material material = materials[currentMaterialIdx];
|
|
TexCoord t = texCoords[vt1_index];
|
|
(*fi).WT(0).u() = t.u;
|
|
(*fi).WT(0).v() = t.v;
|
|
(*fi).WT(0).n() = material.textureIdx;
|
|
|
|
t = texCoords[vt3_index];
|
|
(*fi).WT(1).u() = t.u;
|
|
(*fi).WT(1).v() = t.v;
|
|
(*fi).WT(1).n() = material.textureIdx;
|
|
|
|
t = texCoords[vt4_index];
|
|
(*fi).WT(2).u() = t.u;
|
|
(*fi).WT(2).v() = t.v;
|
|
(*fi).WT(2).n() = material.textureIdx;
|
|
|
|
vt3_index = vt4_index;
|
|
}
|
|
|
|
// verifying validity of vertex index
|
|
// ----------------------------------
|
|
if (v4_index < 0)
|
|
{
|
|
v4_index += numVertices;
|
|
if (v4_index < 0) return E_BAD_VERT_INDEX;
|
|
}
|
|
else if (v4_index > numVertices) return E_BAD_VERT_INDEX;
|
|
else v4_index--; // since index starts from 1 instead of 0
|
|
|
|
if ((v1_index == v4_index) ||
|
|
(v3_index == v4_index))
|
|
result = E_VERTICES_WITH_SAME_IDX_IN_FACE;
|
|
|
|
// assigning face vertices
|
|
// -----------------------
|
|
(*fi).V(0) = &(m.vert[ v1_index ]);
|
|
(*fi).V(1) = &(m.vert[ v3_index ]);
|
|
(*fi).V(2) = &(m.vert[ v4_index ]);
|
|
|
|
// assigning face normal
|
|
// ---------------------
|
|
if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
|
|
{
|
|
// verifying validity of vertex normal index
|
|
// -----------------------------------------
|
|
if (vn4_index < 0)
|
|
{
|
|
vn4_index += numVNormals;
|
|
if (vn4_index < 0) return E_BAD_VERT_NORMAL_INDEX;
|
|
}
|
|
else if (vn4_index > numVNormals) return E_BAD_VERT_NORMAL_INDEX;
|
|
else vn4_index--;
|
|
|
|
|
|
// face normal is computed as an average of wedge normals
|
|
CoordType n = (normals[vn1_index] + normals[vn3_index] + normals[vn4_index]);
|
|
n.Normalize();
|
|
|
|
(*fi).N() = n;
|
|
|
|
(*fi).WN(0) = normals[vn1_index];
|
|
(*fi).WN(1) = normals[vn3_index];
|
|
(*fi).WN(2) = normals[vn4_index];
|
|
|
|
vn3_index = vn4_index;
|
|
}
|
|
else // computing face normal from position of face vertices
|
|
face::ComputeNormalizedNormal(*fi);
|
|
|
|
// assigning face color
|
|
// --------------------
|
|
if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR)
|
|
{
|
|
(*fi).C()[0] = faceColor[0];
|
|
(*fi).C()[1] = faceColor[1];
|
|
(*fi).C()[2] = faceColor[2];
|
|
(*fi).C()[3] = faceColor[3];
|
|
}
|
|
|
|
/*
|
|
// A face polygon composed of more than three vertices is triangulated
|
|
// according to the following schema:
|
|
// v5
|
|
// / \
|
|
// / \
|
|
// / \
|
|
// v1------v4
|
|
// |\ /
|
|
// | \ /
|
|
// | \ /
|
|
// v2---v3
|
|
//
|
|
// As shown above, the 5 vertices polygon (v1,v2,v3,v4,v5)
|
|
// has been split into the triangles (v1,v2,v3), (v1,v3,v4) e (v1,v4,v5).
|
|
// This way vertex v1 becomes the common vertex of all newly generated
|
|
// triangles, and this may lead to the creation of very thin triangles.
|
|
*/
|
|
++fi;
|
|
++numTriangles;
|
|
|
|
v3_index = v4_index;
|
|
}
|
|
|
|
// callback invocation, abort loading process if the call returns false
|
|
if ((cb !=NULL) && (((numTriangles + numVertices)%100)==0) &&
|
|
!(*cb)(static_cast<int>(100.0f * static_cast<float>(numTriangles +
|
|
numVertices) / static_cast<float>(numVerticesPlusFaces)), "Face Loading"))
|
|
return E_ABORTED;
|
|
}
|
|
else if (header.compare("mtllib")==0) // material library
|
|
{
|
|
// obtain the name of the file containing materials library
|
|
std::string materialFileName = tokens[1];
|
|
if (!LoadMaterials( materialFileName.c_str(), materials, m.textures))
|
|
result = E_MATERIAL_FILE_NOT_FOUND;
|
|
}
|
|
else if (header.compare("usemtl")==0) // material usage
|
|
{
|
|
std::string materialName = tokens[1];
|
|
bool found = false;
|
|
unsigned i = 0;
|
|
while (!found && (i < materials.size()))
|
|
{
|
|
std::string currentMaterialName = materials[i].name;
|
|
if (currentMaterialName == materialName)
|
|
{
|
|
currentMaterialIdx = i;
|
|
found = true;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
currentMaterialIdx = 0;
|
|
result = E_MATERIAL_NOT_FOUND;
|
|
}
|
|
}
|
|
// we simply ignore other situations
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} // end of Open
|
|
|
|
|
|
/*!
|
|
* Read the next valid line and parses it into "tokens", allowing
|
|
* the tokens to be read one at a time.
|
|
* \param stream The object providing the input stream
|
|
* \param tokens The "tokens" in the next line
|
|
*/
|
|
inline static const void TokenizeNextLine(std::ifstream &stream, std::vector< std::string > &tokens)
|
|
{
|
|
std::string line;
|
|
do
|
|
std::getline(stream, line, '\n');
|
|
while ((line[0] == '#' || line.length()==0) && !stream.eof()); // skip comments and empty lines
|
|
|
|
if ((line[0] == '#') || (line.length() == 0)) // can be true only on last line of file
|
|
return;
|
|
|
|
size_t from = 0;
|
|
size_t to = 0;
|
|
size_t length = line.size();
|
|
tokens.clear();
|
|
do
|
|
{
|
|
while ((line[from]==' ' || line[from]=='\t') && from!=length)
|
|
from++;
|
|
if(from!=length)
|
|
{
|
|
to = from+1;
|
|
while (line[to]!=' ' && to!=length)
|
|
to++;
|
|
tokens.push_back(line.substr(from, to-from).c_str());
|
|
from = to;
|
|
}
|
|
}
|
|
while (from<length);
|
|
} // end TokenizeNextLine
|
|
|
|
inline static const void SplitVVTToken(std::string token, std::string &vertex, std::string &texcoord)
|
|
{
|
|
vertex.clear();
|
|
texcoord.clear();
|
|
|
|
size_t from = 0;
|
|
size_t to = 0;
|
|
size_t length = token.size();
|
|
|
|
if(from!=length)
|
|
{
|
|
char c = token[from];
|
|
vertex.push_back(c);
|
|
|
|
to = from+1;
|
|
while (to!=length && ((c = token[to]) !='/'))
|
|
{
|
|
vertex.push_back(c);
|
|
++to;
|
|
}
|
|
++to;
|
|
while (to!=length && ((c = token[to]) !=' '))
|
|
{
|
|
texcoord.push_back(c);
|
|
++to;
|
|
}
|
|
}
|
|
} // end of SplitVVTToken
|
|
|
|
inline static const void SplitVVNToken(std::string token, std::string &vertex, std::string &normal)
|
|
{
|
|
vertex.clear();
|
|
normal.clear();
|
|
|
|
size_t from = 0;
|
|
size_t to = 0;
|
|
size_t length = token.size();
|
|
|
|
if(from!=length)
|
|
{
|
|
char c = token[from];
|
|
vertex.push_back(c);
|
|
|
|
to = from+1;
|
|
while (to!=length && ((c = token[to]) !='/'))
|
|
{
|
|
vertex.push_back(c);
|
|
++to;
|
|
}
|
|
++to;
|
|
++to; // should be the second '/'
|
|
while (to!=length && ((c = token[to]) !=' '))
|
|
{
|
|
normal.push_back(c);
|
|
++to;
|
|
}
|
|
}
|
|
} // end of SplitVVNToken
|
|
|
|
inline static const void SplitVVTVNToken(std::string token, std::string &vertex, std::string &texcoord, std::string &normal)
|
|
{
|
|
vertex.clear();
|
|
texcoord.clear();
|
|
normal.clear();
|
|
|
|
size_t from = 0;
|
|
size_t to = 0;
|
|
size_t length = token.size();
|
|
|
|
if(from!=length)
|
|
{
|
|
char c = token[from];
|
|
vertex.push_back(c);
|
|
|
|
to = from+1;
|
|
while (to!=length && ((c = token[to]) !='/'))
|
|
{
|
|
vertex.push_back(c);
|
|
++to;
|
|
}
|
|
++to;
|
|
while (to!=length && ((c = token[to]) !='/'))
|
|
{
|
|
texcoord.push_back(c);
|
|
++to;
|
|
}
|
|
++to;
|
|
while (to!=length && ((c = token[to]) !=' '))
|
|
{
|
|
normal.push_back(c);
|
|
++to;
|
|
}
|
|
}
|
|
} // end of SplitVVTVNToken
|
|
|
|
|
|
static bool LoadMask(const char * filename, int &mask)
|
|
{
|
|
Info oi;
|
|
bool ret=LoadMask(filename, oi);
|
|
mask= oi.mask;
|
|
return ret;
|
|
}
|
|
|
|
/*!
|
|
* Retrieves infos about kind of data stored into the file and fills a mask appropriately
|
|
* \param filename The name of the file to open
|
|
* \param mask A mask which will be filled according to type of data found in the object
|
|
* \param oi A structure which will be filled with infos about the object to be opened
|
|
*/
|
|
static bool LoadMask(const char * filename, Info &oi)
|
|
{
|
|
std::ifstream stream(filename);
|
|
if (stream.fail())
|
|
return false;
|
|
|
|
// obtain length of file:
|
|
stream.seekg (0, std::ios::end);
|
|
int length = stream.tellg();
|
|
if (length == 0) return false;
|
|
stream.seekg (0, std::ios::beg);
|
|
|
|
|
|
bool bHasPerWedgeTexCoord = false;
|
|
bool bHasPerWedgeNormal = false;
|
|
bool bUsingMaterial = false;
|
|
bool bHasPerVertexColor = false;
|
|
bool bHasPerFaceColor = false;
|
|
|
|
std::string header;
|
|
std::vector<std::string> tokens;
|
|
|
|
int numVertices = 0; // stores the number of vertexes been read till now
|
|
int numTriangles = 0; // stores the number of triangular faces been read till now
|
|
|
|
const int deltaPos = 100;
|
|
int currPos = 0;
|
|
int lastPos = currPos;
|
|
|
|
// cycle till we encounter first face
|
|
while (!stream.eof())
|
|
{
|
|
tokens.clear();
|
|
header.clear();
|
|
TokenizeNextLine(stream, tokens);
|
|
|
|
if (tokens.size() > 0)
|
|
{
|
|
header = tokens[0];
|
|
|
|
if (header.compare("v")==0)
|
|
{
|
|
++numVertices;
|
|
if (bUsingMaterial) bHasPerVertexColor = true;
|
|
}
|
|
else if (header.compare("f")==0)
|
|
{
|
|
numTriangles += (static_cast<int>(tokens.size()) - 3);
|
|
std::string remainingText = tokens[1];
|
|
|
|
// we base our assumption on the fact that the way vertex data is
|
|
// referenced into faces must be consistent among the entire file
|
|
int charIdx = 0;
|
|
size_t length = remainingText.size();
|
|
char c;
|
|
while((charIdx != length) && ((c = remainingText[charIdx])!='/') && (c != ' '))
|
|
++charIdx;
|
|
|
|
if (c == '/')
|
|
{
|
|
++charIdx;
|
|
if ((charIdx != length) && ((c = remainingText[charIdx])!='/'))
|
|
{
|
|
bHasPerWedgeTexCoord = true;
|
|
|
|
++charIdx;
|
|
while((charIdx != length) && ((c = remainingText[charIdx])!='/') && (c != ' '))
|
|
++charIdx;
|
|
|
|
if (c == '/')
|
|
bHasPerWedgeNormal = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
bHasPerWedgeNormal = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bUsingMaterial) bHasPerFaceColor = true;
|
|
}
|
|
else if (header.compare("usemtl")==0)
|
|
bUsingMaterial = true;
|
|
}
|
|
|
|
// callback invocation, abort loading process if the call returns false
|
|
if (oi.cb !=NULL)
|
|
{
|
|
currPos = stream.tellg();
|
|
if ((currPos - lastPos) > deltaPos)
|
|
{
|
|
if (!(*oi.cb)((100 * currPos)/length, "Loading mask..."))
|
|
return false;
|
|
lastPos = currPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
// after the encounter of first face we avoid to do additional tests
|
|
while (!stream.eof())
|
|
{
|
|
tokens.clear();
|
|
header.clear();
|
|
TokenizeNextLine(stream, tokens);
|
|
|
|
if (tokens.size() > 0)
|
|
{
|
|
header = tokens[0];
|
|
|
|
if (header.compare("v")==0)
|
|
{
|
|
++numVertices;
|
|
if (bUsingMaterial) bHasPerVertexColor = true;
|
|
}
|
|
else if (header.compare("f")==0)
|
|
{
|
|
numTriangles += (static_cast<int>(tokens.size()) - 3);
|
|
if (bUsingMaterial) bHasPerFaceColor = true;
|
|
}
|
|
else if (header.compare("usemtl")==0)
|
|
bUsingMaterial = true;
|
|
}
|
|
|
|
// callback invocation, abort loading process if the call returns false
|
|
if (oi.cb !=NULL)
|
|
{
|
|
currPos = stream.tellg();
|
|
if ((currPos - lastPos) > deltaPos)
|
|
{
|
|
if (!(*oi.cb)((100 * currPos)/length, "Loading mask..."))
|
|
return false;
|
|
lastPos = currPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
int mask = 0;
|
|
|
|
if (bHasPerWedgeTexCoord)
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
|
|
if (bHasPerWedgeNormal)
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
|
|
if (bHasPerVertexColor)
|
|
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
|
|
if (bHasPerFaceColor)
|
|
mask |= vcg::tri::io::Mask::IOM_FACECOLOR;
|
|
|
|
oi.mask = mask;
|
|
oi.numVertices = numVertices;
|
|
oi.numTriangles = numTriangles;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool LoadMaterials(const char * filename, std::vector<Material> &materials, std::vector<std::string> &textures)
|
|
{
|
|
// assumes we are in the right directory
|
|
|
|
std::ifstream stream(filename);
|
|
if (stream.fail())
|
|
return false;
|
|
|
|
std::vector< std::string > tokens;
|
|
std::string header;
|
|
|
|
materials.clear();
|
|
Material currentMaterial;
|
|
|
|
bool first = true;
|
|
while (!stream.eof())
|
|
{
|
|
tokens.clear();
|
|
TokenizeNextLine(stream, tokens);
|
|
|
|
if (tokens.size() > 0)
|
|
{
|
|
header.clear();
|
|
header = tokens[0];
|
|
|
|
if (header.compare("newmtl")==0)
|
|
{
|
|
if (!first)
|
|
{
|
|
materials.push_back(currentMaterial);
|
|
currentMaterial = Material();
|
|
}
|
|
else
|
|
first = false;
|
|
|
|
strcpy(currentMaterial.name, tokens[1].c_str());
|
|
}
|
|
else if (header.compare("Ka")==0)
|
|
{
|
|
float r = (float) atof(tokens[1].c_str());
|
|
float g = (float) atof(tokens[2].c_str());
|
|
float b = (float) atof(tokens[3].c_str());
|
|
|
|
currentMaterial.ambient = Point3f(r, g, b);
|
|
}
|
|
else if (header.compare("Kd")==0)
|
|
{
|
|
float r = (float) atof(tokens[1].c_str());
|
|
float g = (float) atof(tokens[2].c_str());
|
|
float b = (float) atof(tokens[3].c_str());
|
|
|
|
currentMaterial.diffuse = Point3f(r, g, b);
|
|
}
|
|
else if (header.compare("Ks")==0)
|
|
{
|
|
float r = (float) atof(tokens[1].c_str());
|
|
float g = (float) atof(tokens[2].c_str());
|
|
float b = (float) atof(tokens[3].c_str());
|
|
|
|
currentMaterial.specular = Point3f(r, g, b);
|
|
}
|
|
else if ( (header.compare("d")==0) ||
|
|
(header.compare("Tr")==0) ) // alpha
|
|
{
|
|
currentMaterial.alpha = (float) atof(tokens[1].c_str());
|
|
}
|
|
else if (header.compare("Ns")==0) // shininess
|
|
{
|
|
currentMaterial.shininess = atoi(tokens[1].c_str());
|
|
}
|
|
else if (header.compare("illum")==0) // specular illumination on/off
|
|
{
|
|
int illumination = atoi(tokens[1].c_str());
|
|
currentMaterial.bSpecular = (illumination == 2);
|
|
}
|
|
else if (header.compare("map_Kd")==0) // texture name
|
|
{
|
|
std::string textureName = tokens[1];
|
|
strcpy(currentMaterial.textureFileName, textureName.c_str());
|
|
|
|
// adding texture name into textures vector (if not already present)
|
|
// avoid adding the same name twice
|
|
bool found = false;
|
|
unsigned int size = static_cast<unsigned int>(textures.size());
|
|
unsigned j = 0;
|
|
while (!found && (j < size))
|
|
{
|
|
if (textureName.compare(textures[j])==0)
|
|
{
|
|
currentMaterial.textureIdx = (int)j;
|
|
found = true;
|
|
}
|
|
++j;
|
|
}
|
|
if (!found)
|
|
{
|
|
textures.push_back(textureName);
|
|
currentMaterial.textureIdx = (int)size;
|
|
}
|
|
}
|
|
// we simply ignore other situations
|
|
}
|
|
}
|
|
materials.push_back(currentMaterial); // add last read material
|
|
|
|
stream.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
}; // end class
|
|
} // end Namespace tri
|
|
} // end Namespace io
|
|
} // end Namespace vcg
|
|
|
|
#endif // ndef __VCGLIB_IMPORT_OBJ
|