Commit ba72c5b00e4be25f60dabff901344b282132261e
1 parent
27b826a8
added OBJ class
Showing
2 changed files
with
528 additions
and
0 deletions
Show diff stats
stim/envi/binary.h
... | ... | @@ -71,6 +71,16 @@ protected: |
71 | 71 | //open the file as binary for reading and writing |
72 | 72 | file.open(filename.c_str(), std::ios::in | std::ios::out | std::ios::binary); |
73 | 73 | |
74 | + //if the file isn't open, the user may only have read access | |
75 | + if(!file.is_open()){ | |
76 | + std::cout<<"class BINARY - failed to open file, trying for read only"<<std::endl; | |
77 | + file.open(filename.c_str(), std::ios::in | std::ios::binary); | |
78 | + if(!file.is_open()){ | |
79 | + std::cout<<" still unable to load the file"<<std::endl; | |
80 | + return false; | |
81 | + } | |
82 | + } | |
83 | + | |
74 | 84 | //if the file is successful |
75 | 85 | if(file){ |
76 | 86 | name = filename; //set the name | ... | ... |
1 | +#ifndef STIM_OBJ_H | |
2 | +#define STIM_OBJ_H | |
3 | + | |
4 | +#include <vector> | |
5 | +#include <sstream> | |
6 | +#include <fstream> | |
7 | +#include <stdlib.h> | |
8 | +#include <stim/parser/parser.h> | |
9 | + | |
10 | +namespace stim{ | |
11 | + | |
12 | +/** This class provides an interface for loading, saving, and working with Wavefront OBJ files. | |
13 | + * The class structure uses similar terminology to the OBJ file structure, so one familiar with it | |
14 | + * should be able to work well with this class. | |
15 | + * | |
16 | + * This class currently provides the ability to load explicitly defined geometric objects, including point lists, | |
17 | + * lines, and faces with positions, texture coordinates, and normals. This data is stored in a protected series | |
18 | + * of lists that are accessible to classes that extend this one. | |
19 | + * | |
20 | + * Multiple helper classes are used to facilitate loading, storing, and saving geometry. These are protected | |
21 | + * and therefore only designed to be used within the confines of the class. These helper classes include: | |
22 | + * | |
23 | + * vertex class - contains methods for loading, saving, and storing 1, 2, 3, and 4 component vectors | |
24 | + * triplet class - contains methods for loading, saving, and storing indexed vertices used for geometric objects | |
25 | + * geometry class - contains a list of triplets used to define a geometric structure, such as a face or line | |
26 | + */ | |
27 | + | |
28 | +enum obj_type { OBJ_NONE, OBJ_LINE, OBJ_FACE, OBJ_POINTS }; | |
29 | + | |
30 | +template <typename T> | |
31 | +class obj{ | |
32 | + | |
33 | +protected: | |
34 | + | |
35 | + enum token_type { OBJ_INVALID, OBJ_V, OBJ_VT, OBJ_VN, OBJ_P, OBJ_L, OBJ_F }; | |
36 | + | |
37 | + struct vertex : public std::vector<T>{ | |
38 | + | |
39 | + using std::vector<T>::push_back; | |
40 | + using std::vector<T>::size; | |
41 | + using std::vector<T>::at; | |
42 | + | |
43 | + vertex(){} | |
44 | + | |
45 | + //constructors for quickly building vertices with arbitrary numbers of components | |
46 | + vertex(T x){ | |
47 | + push_back(x); | |
48 | + } | |
49 | + | |
50 | + vertex(T x, T y){ | |
51 | + push_back(x); | |
52 | + push_back(y); | |
53 | + } | |
54 | + | |
55 | + vertex(T x, T y, T z){ | |
56 | + push_back(x); | |
57 | + push_back(y); | |
58 | + push_back(z); | |
59 | + } | |
60 | + | |
61 | + vertex(T x, T y, T z, T w){ | |
62 | + push_back(x); | |
63 | + push_back(y); | |
64 | + push_back(z); | |
65 | + push_back(w); | |
66 | + } | |
67 | + | |
68 | + //constructor creates a vertex from a line string | |
69 | + vertex(std::string line){ | |
70 | + | |
71 | + //create a temporary vector used to store string tokens | |
72 | + std::vector<std::string> tokens = stim::parser::split(line, ' '); | |
73 | + | |
74 | + //create temporary storage for casting the token values | |
75 | + T val; | |
76 | + | |
77 | + //for each token (skipping the first) | |
78 | + for(unsigned int i = 1; i < tokens.size(); i++){ | |
79 | + std::stringstream ss(tokens[i]); //create a stringstream object for casting | |
80 | + ss>>val; //cast the token to the correct numerical value | |
81 | + push_back(val); //push the coordinate into the vertex | |
82 | + } | |
83 | + } | |
84 | + | |
85 | + //output the vertex as a string | |
86 | + std::string str(){ | |
87 | + | |
88 | + std::stringstream ss; | |
89 | + | |
90 | + for(int i = 0; i < size(); i++){ | |
91 | + if(i > 0) | |
92 | + ss<<' '; | |
93 | + | |
94 | + ss<<at(i); | |
95 | + } | |
96 | + | |
97 | + return ss.str(); //return the vertex string | |
98 | + } | |
99 | + | |
100 | + | |
101 | + }; //end vertex | |
102 | + | |
103 | + //triplet used to specify geometric vertices consisting of a position vertex, texture vertex, and normal | |
104 | + struct triplet : public std::vector<unsigned int>{ | |
105 | + | |
106 | + //create a triplet given a parameter list (OBJ indices start at 1, so 0 can be used to indicate no value) | |
107 | + triplet(unsigned int v, unsigned int vt = 0, unsigned int vn = 0){ | |
108 | + push_back(v); | |
109 | + if(vn != 0){ | |
110 | + push_back(vt); | |
111 | + push_back(vn); | |
112 | + } | |
113 | + else if(vt != 0) | |
114 | + push_back(vt); | |
115 | + } | |
116 | + | |
117 | + //build a triplet from a string | |
118 | + triplet(std::string s){ | |
119 | + | |
120 | + //create a temporary vector used to store string tokens | |
121 | + std::vector<std::string> tokens = stim::parser::split(s, '\\'); | |
122 | + | |
123 | + //std::cout<<"processing triplet: "<<s<<std::endl; | |
124 | + if(tokens.size() >= 1) | |
125 | + push_back( atoi( tokens[0].c_str() ) ); | |
126 | + | |
127 | + if(tokens.size() >= 2){ | |
128 | + if(tokens[1].length()) | |
129 | + push_back( atoi( tokens[1].c_str() ) ); | |
130 | + else | |
131 | + push_back(0); | |
132 | + } | |
133 | + | |
134 | + if(tokens.size() == 3) | |
135 | + push_back( atoi( tokens[2].c_str() ) ); | |
136 | + | |
137 | + } | |
138 | + | |
139 | + //output the geometry as a string | |
140 | + std::string str(){ | |
141 | + | |
142 | + std::stringstream ss; | |
143 | + | |
144 | + ss<<at(0); //the vertex is always the first value | |
145 | + | |
146 | + if(size() == 3){ | |
147 | + if(at(1) == 0) | |
148 | + ss<<"\\\\"<<at(2); | |
149 | + else | |
150 | + ss<<'\\'<<at(1)<<'\\'<<at(2); | |
151 | + } | |
152 | + else if(size() == 2) | |
153 | + ss<<"\\"<<at(1); | |
154 | + | |
155 | + return ss.str(); | |
156 | + } | |
157 | + | |
158 | + }; //end triplet | |
159 | + | |
160 | + //geometry used to specify a geometric structure consisting of several triplets | |
161 | + struct geometry : public std::vector<triplet>{ | |
162 | + | |
163 | + using std::vector<triplet>::size; | |
164 | + using std::vector<triplet>::at; | |
165 | + using std::vector<triplet>::push_back; | |
166 | + | |
167 | + geometry(){} | |
168 | + | |
169 | + //constructs a geometry object from a line read from an OBJ file | |
170 | + geometry(std::string line){ | |
171 | + | |
172 | + //create a temporary vector used to store string tokens | |
173 | + std::vector<std::string> tokens = stim::parser::split(line, ' '); | |
174 | + | |
175 | + //for each triplet in the line | |
176 | + for(unsigned int i = 1; i < tokens.size(); i++){ | |
177 | + //construct the triplet and push it to the geometry | |
178 | + push_back( triplet(tokens[i]) ); | |
179 | + } | |
180 | + | |
181 | + } | |
182 | + | |
183 | + std::string str(){ | |
184 | + | |
185 | + std::stringstream ss; | |
186 | + | |
187 | + for(unsigned int i = 0; i < size(); i++){ | |
188 | + | |
189 | + if(i != 0) | |
190 | + ss<<' '; | |
191 | + | |
192 | + ss<<at(i).str(); | |
193 | + } | |
194 | + | |
195 | + return ss.str(); | |
196 | + } | |
197 | + }; | |
198 | + | |
199 | + std::vector<vertex> V; //vertex spatial position | |
200 | + std::vector<vertex> VN; | |
201 | + std::vector<vertex> VT; | |
202 | + | |
203 | + //structure lists | |
204 | + std::vector<geometry> L; //list of lines | |
205 | + std::vector<geometry> P; //list of points structures | |
206 | + std::vector<geometry> F; //list of faces | |
207 | + | |
208 | + //information for the current geometric object | |
209 | + geometry current_geo; | |
210 | + vertex current_vt; | |
211 | + vertex current_vn; | |
212 | + | |
213 | + //flags for the current geometric object | |
214 | + obj_type current_type; | |
215 | + bool geo_flag_vt; | |
216 | + bool geo_flag_vn; | |
217 | + bool vert_flag_vt; | |
218 | + bool vert_flag_vn; | |
219 | + | |
220 | + void update_vt(vertex vt){ | |
221 | + current_vt = vt; | |
222 | + | |
223 | + //the geometry vertex texture flag can only be set for the first vertex | |
224 | + if(current_geo.size() == 0) | |
225 | + geo_flag_vt = true; | |
226 | + | |
227 | + vert_flag_vt = true; | |
228 | + } | |
229 | + | |
230 | + void update_vn(vertex vn){ | |
231 | + current_vn = vn; | |
232 | + | |
233 | + //the geometry normal flag can only be set for the first vertex | |
234 | + if(current_geo.size() == 0) | |
235 | + geo_flag_vn = true; | |
236 | + | |
237 | + vert_flag_vn = true; | |
238 | + } | |
239 | + | |
240 | + //create a triple and add it to the current geometry | |
241 | + void update_v(vertex vv){ | |
242 | + | |
243 | + unsigned int v; | |
244 | + unsigned int vt = 0; | |
245 | + unsigned int vn = 0; | |
246 | + | |
247 | + //if the current geometry is using a texture coordinate, add the current texture coordinate to the geometry | |
248 | + if(geo_flag_vt){ | |
249 | + if(vert_flag_vt) //don't add it more than once | |
250 | + VT.push_back(current_vt); | |
251 | + vt = VT.size(); | |
252 | + } | |
253 | + | |
254 | + //if the current geometry is using a normal, add the current texture coordinate to the geometry | |
255 | + if(geo_flag_vn){ | |
256 | + if(vert_flag_vn) //don't add it more than once | |
257 | + VN.push_back(current_vn); | |
258 | + vn = VN.size(); | |
259 | + } | |
260 | + | |
261 | + //add the current vertex position to the geometry | |
262 | + V.push_back(vv); | |
263 | + v = V.size(); | |
264 | + | |
265 | + //create a triplet and add it to the current geometry | |
266 | + current_geo.push_back(triplet(v, vt, vn)); | |
267 | + | |
268 | + //clear the vertex flags | |
269 | + vert_flag_vt = false; | |
270 | + vert_flag_vn = false; | |
271 | + } | |
272 | + | |
273 | + void init(){ | |
274 | + //clear all lists | |
275 | + V.clear(); | |
276 | + VT.clear(); | |
277 | + VN.clear(); | |
278 | + P.clear(); | |
279 | + L.clear(); | |
280 | + F.clear(); | |
281 | + | |
282 | + //initialize all of the flags | |
283 | + current_type = OBJ_NONE; | |
284 | + geo_flag_vt = false; | |
285 | + geo_flag_vn = false; | |
286 | + vert_flag_vt = false; | |
287 | + vert_flag_vn = false; | |
288 | + } | |
289 | + | |
290 | + //gets the type of token representing the entry in the OBJ file | |
291 | + token_type get_token(std::string s){ | |
292 | + | |
293 | + //if the line contains a vertex | |
294 | + if(s[0] == 'v'){ | |
295 | + if(s[1] == ' ') return OBJ_V; | |
296 | + if(s[1] == 't') return OBJ_VT; | |
297 | + if(s[1] == 'n') return OBJ_VN; | |
298 | + } | |
299 | + | |
300 | + if(s[0] == 'l' && s[1] == ' ') return OBJ_L; | |
301 | + if(s[0] == 'p' && s[1] == ' ') return OBJ_P; | |
302 | + if(s[0] == 'f' && s[1] == ' ') return OBJ_F; | |
303 | + | |
304 | + return OBJ_INVALID; | |
305 | + } | |
306 | + | |
307 | +public: | |
308 | + //functions for setting the texture coordinate for the next vertex | |
309 | + void TexCoord(T x){ update_vt(vertex(x));} | |
310 | + void TexCoord(T x, T y){ update_vt(vertex(x, y));} | |
311 | + void TexCoord(T x, T y, T z){ update_vt(vertex(x, y, z));} | |
312 | + void TexCoord(T x, T y, T z, T w){ update_vt(vertex(x, y, z, w));} | |
313 | + | |
314 | + //functions for setting the normal for the next vertex | |
315 | + void Normal(T x){ update_vn(vertex(x));} | |
316 | + void Normal(T x, T y){ update_vn(vertex(x, y));} | |
317 | + void Normal(T x, T y, T z){ update_vn(vertex(x, y, z));} | |
318 | + void Normal(T x, T y, T z, T w){ update_vn(vertex(x, y, z, w));} | |
319 | + | |
320 | + //functions for setting the next vertex position (note that this updates the current geometry) | |
321 | + void Vertex(T x){ update_v(vertex(x));} | |
322 | + void Vertex(T x, T y){ update_v(vertex(x, y));} | |
323 | + void Vertex(T x, T y, T z){ update_v(vertex(x, y, z));} | |
324 | + void Vertex(T x, T y, T z, T w){ update_v(vertex(x, y, z, w));} | |
325 | + | |
326 | + ///This function starts drawing of a primitive object, such as a line, face, or point set | |
327 | + | |
328 | + /// @param t is the type of object to be drawn: OBJ_POINTS, OBJ_LINE, OBJ_FACE | |
329 | + void Begin(obj_type t){ | |
330 | + current_type = t; | |
331 | + } | |
332 | + | |
333 | + /// This function terminates drawing of a primitive object, such as a line, face, or point set | |
334 | + void End(){ | |
335 | + | |
336 | + //copy the current object to the appropriate list | |
337 | + switch(current_type){ | |
338 | + | |
339 | + case OBJ_NONE: | |
340 | + std::cout<<"STIM::OBJ error, objEnd() called before objBegin()."<<std::endl; | |
341 | + break; | |
342 | + | |
343 | + case OBJ_POINTS: | |
344 | + P.push_back(current_geo); | |
345 | + break; | |
346 | + | |
347 | + case OBJ_LINE: | |
348 | + L.push_back(current_geo); | |
349 | + break; | |
350 | + | |
351 | + case OBJ_FACE: | |
352 | + F.push_back(current_geo); | |
353 | + } | |
354 | + | |
355 | + //clear everything | |
356 | + current_type = OBJ_NONE; | |
357 | + current_geo.clear(); | |
358 | + vert_flag_vt = false; | |
359 | + vert_flag_vn = false; | |
360 | + geo_flag_vt = false; | |
361 | + geo_flag_vn = false; | |
362 | + } | |
363 | + | |
364 | + //output the OBJ structure as a string | |
365 | + std::string str(){ | |
366 | + | |
367 | + std::stringstream ss; | |
368 | + | |
369 | + unsigned int i; | |
370 | + | |
371 | + //output all of the vertices | |
372 | + if(V.size()){ | |
373 | + ss<<"#vertex positions"<<std::endl; | |
374 | + for(i = 0; i < V.size(); i++){ | |
375 | + ss<<"v "<<V[i].str()<<std::endl; | |
376 | + } | |
377 | + } | |
378 | + | |
379 | + //output all of the texture coordinates | |
380 | + if(VT.size()){ | |
381 | + ss<<std::endl<<"#vertex texture coordinates"<<std::endl; | |
382 | + for(i = 0; i < VT.size(); i++){ | |
383 | + ss<<"vt "<<VT[i].str()<<std::endl; | |
384 | + } | |
385 | + } | |
386 | + | |
387 | + //output all of the normals | |
388 | + if(VN.size()){ | |
389 | + ss<<std::endl<<"#vertex normals"<<std::endl; | |
390 | + for(i = 0; i < VN.size(); i++){ | |
391 | + ss<<"vn "<<VN[i].str()<<std::endl; | |
392 | + } | |
393 | + } | |
394 | + | |
395 | + //output all of the points | |
396 | + if(P.size()){ | |
397 | + ss<<std::endl<<"#point structures"<<std::endl; | |
398 | + for(i = 0; i < P.size(); i++){ | |
399 | + ss<<"p "<<P[i].str()<<std::endl; | |
400 | + } | |
401 | + } | |
402 | + | |
403 | + //output all of the lines | |
404 | + if(L.size()){ | |
405 | + ss<<std::endl<<"#line structures"<<std::endl; | |
406 | + for(i = 0; i < L.size(); i++){ | |
407 | + ss<<"l "<<L[i].str()<<std::endl; | |
408 | + } | |
409 | + } | |
410 | + | |
411 | + //output all of the lines | |
412 | + if(F.size()){ | |
413 | + ss<<std::endl<<"#face structures"<<std::endl; | |
414 | + for(i = 0; i < F.size(); i++){ | |
415 | + ss<<"f "<<F[i].str()<<std::endl; | |
416 | + } | |
417 | + } | |
418 | + | |
419 | + return ss.str(); //return the constructed string | |
420 | + } | |
421 | + | |
422 | + obj(){ | |
423 | + init(); //private function that initializes everything | |
424 | + } | |
425 | + | |
426 | + void clear(){ | |
427 | + init(); | |
428 | + } | |
429 | + | |
430 | + /// This function saves the current structure as a Wavefront OBJ file | |
431 | + | |
432 | + /// @param filename is the name of the file to be saved | |
433 | + bool save(std::string filename){ | |
434 | + | |
435 | + std::ofstream outfile(filename.c_str()); | |
436 | + | |
437 | + if(!outfile){ | |
438 | + std::cout<<"STIM::OBJ error opening file for writing"<<std::endl; | |
439 | + return false; | |
440 | + } | |
441 | + | |
442 | + //output the OBJ data to the file | |
443 | + outfile<<str(); | |
444 | + | |
445 | + //close the file | |
446 | + outfile.close(); | |
447 | + | |
448 | + return true; | |
449 | + } | |
450 | + | |
451 | + /// Load a Wavefront OBJ file (support for faces, lines, and point lists) | |
452 | + | |
453 | + /// @param filename is the name of the OBJ file to load | |
454 | + bool load(std::string filename){ | |
455 | + | |
456 | + //load the file | |
457 | + std::ifstream infile(filename.c_str()); | |
458 | + | |
459 | + //if the file is invalid, throw an error | |
460 | + if(!infile){ | |
461 | + std::cout<<"STIM::OBJ Error loading file "<<filename<<std::endl; | |
462 | + return false; | |
463 | + } | |
464 | + | |
465 | + std::string line; | |
466 | + while(infile){ | |
467 | + | |
468 | + //create a variable to store a single line | |
469 | + getline(infile, line); | |
470 | + | |
471 | + //get the token representing the first parameter | |
472 | + token_type token = get_token(line); | |
473 | + | |
474 | + switch(token){ | |
475 | + | |
476 | + case OBJ_INVALID: | |
477 | + break; | |
478 | + | |
479 | + case OBJ_V: | |
480 | + V.push_back(vertex(line)); | |
481 | + break; | |
482 | + | |
483 | + case OBJ_VT: | |
484 | + VT.push_back(vertex(line)); | |
485 | + break; | |
486 | + | |
487 | + case OBJ_VN: | |
488 | + VN.push_back(vertex(line)); | |
489 | + break; | |
490 | + | |
491 | + case OBJ_F: | |
492 | + F.push_back(geometry(line)); | |
493 | + break; | |
494 | + | |
495 | + }; | |
496 | + } | |
497 | + | |
498 | + | |
499 | + | |
500 | + //close the file | |
501 | + infile.close(); | |
502 | + | |
503 | + return true; | |
504 | + | |
505 | + } | |
506 | + | |
507 | + | |
508 | +}; | |
509 | + | |
510 | + | |
511 | + | |
512 | + | |
513 | + | |
514 | + | |
515 | +}; | |
516 | + | |
517 | + | |
518 | +#endif | ... | ... |