NGL  6.5
The NCCA Graphics Library
Obj.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 Jon Macey
3 
4  This program is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #include "boost/bind.hpp"
18 
19 #include "boost/spirit.hpp"
21 #include "Obj.h"
22 //----------------------------------------------------------------------------------------------------------------------
25 //----------------------------------------------------------------------------------------------------------------------
26 
27 
28 namespace ngl
29 {
30 
31 // make a namespace for our parser to save writing boost::spirit:: all the time
32 namespace spt=boost::spirit;
33 
34 // syntactic sugar for specifying our grammar
35 typedef spt::rule<spt::phrase_scanner_t> srule;
36 
37 
38 //----------------------------------------------------------------------------------------------------------------------
39 // parse a vertex
40 void Obj::parseVertex( const char *_begin ) noexcept
41 {
42  std::vector<Real> values;
43  // here is the parse rule to load the data into a vector (above)
44  srule vertex = "v" >> spt::real_p[spt::append(values)] >>
45  spt::real_p[spt::append(values)] >>
46  spt::real_p[spt::append(values)];
47  // now parse the data
48  spt::parse_info<> result = spt::parse(_begin, vertex, spt::space_p);
49  // should check this at some stage
50  NGL_UNUSED(result);
51  // and add it to our vert list in abstact mesh parent
52  m_verts.push_back(Vec3(values[0],values[1],values[2]));
53 }
54 
55 
56 //----------------------------------------------------------------------------------------------------------------------
57 // parse a texture coordinate
58 void Obj::parseTextureCoordinate(const char * _begin ) noexcept
59 {
60  std::vector<Real> values;
61  // generate our parse rule for a tex cord,
62  // this can be either a 2 or 3 d text so the *rule looks for an additional one
63  srule texcord = "vt" >> spt::real_p[spt::append(values)] >>
64  spt::real_p[spt::append(values)] >>
65  *(spt::real_p[spt::append(values)]);
66  spt::parse_info<> result = spt::parse(_begin, texcord, spt::space_p);
67  // should check the return values at some stage
68  NGL_UNUSED(result);
69 
70  // build tex cord
71  // if we have a value use it other wise set to 0
72  Real vt3 = values.size() == 3 ? values[2] : 0.0f;
73  m_tex.push_back(Vec3(values[0],values[1],vt3));
74 }
75 
76 //----------------------------------------------------------------------------------------------------------------------
77 // parse a normal
78 void Obj::parseNormal( const char *_begin ) noexcept
79 {
80  std::vector<Real> values;
81  // here is our rule for normals
82  srule norm = "vn" >> spt::real_p[spt::append(values)] >>
83  spt::real_p[spt::append(values)] >>
84  spt::real_p[spt::append(values)];
85  // parse and push back to the list
86  spt::parse_info<> result = spt::parse(_begin, norm, spt::space_p);
87  // should check the return values at some stage
88  NGL_UNUSED(result);
89  m_norm.push_back(Vec3(values[0],values[1],values[2]));
90 }
91 
92 //----------------------------------------------------------------------------------------------------------------------
93 // parse face
94 void Obj::parseFace(const char * _begin ) noexcept
95 {
96  // ok this one is quite complex first create some lists for our face data
97  // list to hold the vertex data indices
98  std::vector<unsigned int> vec;
99  // list to hold the tex cord indices
100  std::vector<unsigned int> tvec;
101  // list to hold the normal indices
102  std::vector<unsigned int> nvec;
103 
104  // create the parse rule for a face entry V/T/N
105  // so our entry can be always a vert, followed by optional t and norm seperated by /
106  // also it is possible to have just a V value with no / so the rule should do all this
107  srule entry = spt::int_p[spt::append(vec)] >>
108  (
109  ("/" >> (spt::int_p[spt::append(tvec)] | spt::epsilon_p) >>
110  "/" >> (spt::int_p[spt::append(nvec)] | spt::epsilon_p)
111  )
112  | spt::epsilon_p
113  );
114  // a face has at least 3 of the above entries plus many optional ones
115  srule face = "f" >> entry >> entry >> entry >> *(entry);
116  // now we've done this we can parse
117  spt::parse(_begin, face, spt::space_p);
118 
119  unsigned int numVerts=static_cast<unsigned int>(vec.size());
120  // so now build a face structure.
121  Face f;
122  // verts are -1 the size
123  f.m_numVerts=numVerts-1;
124  f.m_textureCoord=false;
125  f.m_normals=false;
126  // copy the vertex indices into our face data structure index in obj start from 1
127  // so we need to do -1 for our array index
128  for(auto i : vec)
129  {
130  f.m_vert.push_back(i-1);
131  }
132 
133  // merge in texture coordinates and normals, if present
134  // OBJ format requires an encoding for faces which uses one of the vertex/texture/normal specifications
135  // consistently across the entire face. eg. we can have all v/vt/vn, or all v//vn, or all v, but not
136  // v//vn then v/vt/vn ...
137  if(!nvec.empty())
138  {
139  if(nvec.size() != vec.size())
140  {
141  std::cerr <<"Something wrong with the face data will continue but may not be correct\n";
142  }
143 
144  // copy in these references to normal vectors to the mesh's normal vector
145  for(auto i : nvec)
146  {
147  f.m_norm.push_back(i-1);
148  }
149  f.m_normals=true;
150 
151  }
152 
153  //
154  // merge in texture coordinates, if present
155  //
156  if(!tvec.empty())
157  {
158  if(tvec.size() != vec.size())
159  {
160  std::cerr <<"Something wrong with the face data will continue but may not be correct\n";
161  }
162 
163  // copy in these references to normal vectors to the mesh's normal vector
164  for(auto i : tvec)
165  {
166  f.m_tex.push_back(i-1);
167  }
168 
169  f.m_textureCoord=true;
170 
171  }
172 // finally save the face into our face list
173  m_face.push_back(f);
174 }
175 
176 //----------------------------------------------------------------------------------------------------------------------
177 bool Obj::load(const std::string &_fname,bool _calcBB ) noexcept
178 {
179  // here we build up our ebnf rules for parsing
180  // so first we have a comment
181  srule comment = spt::comment_p("#");
182 
183  // see below for the rest of the obj spec and other good format data
184  // http://local.wasp.uwa.edu.au/~pbourke/dataformats/obj/
185 
186  // vertices rule v is a parse of 3 reals and we run the parseVertex function
187  srule vertex= ("v" >> spt::real_p >> spt::real_p >> spt::real_p) [bind(&Obj::parseVertex,boost::ref(*this), _1)];
189  srule tex= ("vt" >> spt::real_p >> spt::real_p) [bind(&Obj::parseTextureCoordinate, boost::ref(*this), _1)];
190  // the normal rule and parsing function
191  srule norm= ("vn" >> spt::real_p >> spt::real_p >> spt::real_p) [bind(&Obj::parseNormal,boost::ref(*this), _1)];
192 
193  // our vertex data can be any of the above values
194  srule vertex_type = vertex | tex | norm;
195 
196  // the rule for the face and parser
197  srule face = (spt::ch_p('f') >> *(spt::anychar_p))[bind(&Obj::parseFace, boost::ref(*this), _1)];
198  // open the file to parse
199  std::ifstream in(_fname.c_str());
200  if (in.is_open() != true)
201  {
202  std::cout<<"FILE NOT FOUND !!!! "<<_fname.c_str()<<"\n";
203  return false;
204 
205  }
206  std::string str;
207  // loop grabbing a line and then pass it to our parsing framework
208  while(std::getline(in, str))
209  {
210  spt::parse(str.c_str(), vertex_type | face | comment, spt::space_p);
211  }
212  // now we are done close the file
213  in.close();
214 
215  // grab the sizes used for drawing later
216  m_nVerts=static_cast<unsigned int>(m_verts.size());
217  m_nNorm=static_cast<unsigned int>(m_norm.size());
218  m_nTex=static_cast<unsigned int>(m_tex.size());
219  m_nFaces=static_cast<unsigned int>(m_face.size());
220 
221 
222  // Calculate the center of the object.
223  if(_calcBB == true)
224  {
225  this->calcDimensions();
226  }
227  return true;
228 
229 }
230 
231 //----------------------------------------------------------------------------------------------------------------------
232 Obj::Obj(const std::string& _fname , bool _calcBB) noexcept :AbstractMesh()
233 {
234  m_vbo=false;
235  m_ext=0;
236  // set default values
237  m_nVerts=m_nNorm=m_nTex=m_nFaces=0;
238  //set the default extents to 0
239  m_maxX=0.0f; m_maxY=0.0f; m_maxZ=0.0f;
240  m_minX=0.0f; m_minY=0.0f; m_minZ=0.0f;
241  m_nNorm=m_nTex=0;
242 
243  // load the file in
244  m_loaded=load(_fname,_calcBB);
245 
246  m_texture = false;
247 }
248 
249 //----------------------------------------------------------------------------------------------------------------------
250 Obj::Obj( const char *_fname,const char *_texName, bool _calcBB ) noexcept:AbstractMesh()
251 {
252  m_vbo=false;
253  m_vao=false;
254  m_ext=0;
255  // set default values
256  m_nVerts=m_nNorm=m_nTex=m_nFaces=0;
257  //set the default extents to 0
258  m_maxX=0.0f; m_maxY=0.0f; m_maxZ=0.0f;
259  m_minX=0.0f; m_minY=0.0f; m_minZ=0.0f;
260  m_nNorm=m_nTex=0;
261  // load the file in
262  m_loaded=load(_fname,_calcBB);
263 
264  // load texture
265  loadTexture(_texName);
266  m_texture = true;
267 
268 }
269 
270 Obj::Obj( const std::string& _fname,const std::string& _texName, bool _calcBB ) noexcept:AbstractMesh()
271 {
272  m_vbo=false;
273  m_vao=false;
274  m_ext=0;
275  // set default values
276  m_nVerts=m_nNorm=m_nTex=m_nFaces=0;
277  //set the default extents to 0
278  m_maxX=0.0f; m_maxY=0.0f; m_maxZ=0.0f;
279  m_minX=0.0f; m_minY=0.0f; m_minZ=0.0f;
280  m_nNorm=m_nTex=0;
281  // load the file in
282  m_loaded=load(_fname,_calcBB);
283 
284  // load texture
285  loadTexture(_texName);
286  m_texture = true;
287 }
288 
289 //----------------------------------------------------------------------------------------------------------------------
290 void Obj::save(const std::string& _fname)const noexcept
291 {
292  // Open the stream and parse
293  std::fstream fileOut;
294  fileOut.open(_fname.c_str(),std::ios::out);
295  if (!fileOut.is_open())
296  {
297  std::cout <<"File : "<<_fname<<" Not founds "<<std::endl;
298  return;
299  }
300  // write out some comments
301  fileOut<<"# This file was created by ngl Obj exporter "<<_fname.c_str()<<std::endl;
302  // was c++ 11 for(Vec3 v : m_norm) for all of these
303  // write out the verts
304  for(Vec3 v : m_verts)
305  {
306  fileOut<<"v "<<v.m_x<<" "<<v.m_y<<" "<<v.m_z<<std::endl;
307  }
308 
309  // write out the tex cords
310  for(Vec3 v : m_tex)
311  {
312  fileOut<<"vt "<<v.m_x<<" "<<v.m_y<<std::endl;
313  }
314  // write out the normals
315 
316  for(Vec3 v : m_norm)
317  {
318  fileOut<<"vn "<<v.m_x<<" "<<v.m_y<<" "<<v.m_z<<std::endl;
319  }
320 
321  // finally the faces
322  for(Face f : m_face)
323  {
324  fileOut<<"f ";
325  // we now have V/T/N for each to write out
326  for(unsigned int i=0; i<=f.m_numVerts; ++i)
327  {
328  // don't forget that obj indices start from 1 not 0 (i did originally !)
329  fileOut<<f.m_vert[i]+1;
330  fileOut<<"/";
331  fileOut<<f.m_tex[i]+1;
332  fileOut<<"/";
333 
334  fileOut<<f.m_norm[i]+1;
335  fileOut<<" ";
336  }
337  fileOut<<std::endl;
338  }
339 }
340 
341 } //end ngl namespace
342 //----------------------------------------------------------------------------------------------------------------------
343 
344 
345 
std::vector< uint32_t > m_tex
The texture co-ord index.
Definition: AbstractMesh.h:56
#define NGL_UNUSED(arg)
define unused to quiet Warnings
Definition: Types.h:143
std::vector< uint32_t > m_norm
the normal index
Definition: AbstractMesh.h:60
void save(const std::string &_fname) const noexcept
method to save the obj
Definition: Obj.cpp:290
std::vector< uint32_t > m_vert
The vertices index.
Definition: AbstractMesh.h:52
virtual void parseVertex(const char *_begin) noexcept
parser function to parse the vertex used by boost::spirit parser
Definition: Obj.cpp:40
simple Vec3 encapsulates a 3 float object like glsl vec3 but not maths use the Vec3 class for maths a...
Definition: Vec3.h:51
implementation files for RibExport class
Definition: AABB.cpp:22
GLuint in
Definition: glew.h:11550
GLint GLsizei const GLuint64 * values
Definition: glew.h:3624
PRECISION Real
create a variable called Real which is the main data type we use (GLfloat for most cases) ...
Definition: Types.h:127
GLuint64EXT * result
Definition: glew.h:14309
virtual void parseFace(const char *_begin) noexcept
parser function to parse the Face data used by boost::spirit parser
Definition: Obj.cpp:94
an abstract base mesh used to build specific meshes such as Obj
Definition: AbstractMesh.h:105
const GLdouble * v
Definition: glew.h:1394
simple class used to encapsulate a single face of an abstract mesh file
Definition: AbstractMesh.h:42
spt::rule< spt::phrase_scanner_t > srule
Definition: Obj.cpp:35
basic obj loader inherits from AbstractMesh
virtual void parseTextureCoordinate(const char *_begin) noexcept
parser function to parse the text cord used by boost::spirit parser
Definition: Obj.cpp:58
virtual void parseNormal(const char *_begin) noexcept
parser function to parse the Norma used by boost::spirit parser
Definition: Obj.cpp:78
bool load(const std::string &_fname, bool _calcBB=true) noexcept
Method to load the file in.
Definition: Obj.cpp:177
bool m_textureCoord
debug flag to turn face on and off
Definition: AbstractMesh.h:64
GLenum GLint ref
Definition: glew.h:1848
unsigned int m_numVerts
the number of vertices in the face
Definition: AbstractMesh.h:48
GLsizei const GLchar *const * string
Definition: glew.h:1847
Obj() noexcept
default constructor
Definition: Obj.h:61
GLenum GLuint GLint GLenum face
Definition: glew.h:4628
bool m_normals
Definition: AbstractMesh.h:65
GLclampf f
Definition: glew.h:3511