Commit ba72c5b00e4be25f60dabff901344b282132261e

Authored by David Mayerich
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
... ...
stim/visualization/obj.h 0 → 100644
  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
... ...