objJedi.h 45.8 KB
``````#ifndef OBJJEDI_H
#define OBJJEDI_H

One aspect of the writer is based on OpenGL output commands.  You can send OpenGL commands
(replacing "gl" and "GL" with "obj" and "OBJ" respectively to render to an Wavefront OBJ file
*/

//#include "rtsMath.h"
#include "rtsLinearAlgebra.h"
#include <vector>
#include <fstream>
#include <iostream>
#include <string.h>
using namespace std;

#define OBJ_V_X		0x1
#define OBJ_V_Y		0x2
#define OBJ_V_Z		0x4
#define OBJ_V_W		0x8

#define OBJ_VT_U	0x1
#define OBJ_VT_V	0x2
#define OBJ_VT_W	0x4

#define OBJ_VN_I	0x1
#define OBJ_VN_J	0x2
#define OBJ_VN_K	0x4

#define OBJ_V		0x1
#define OBJ_VT		0x2
#define OBJ_VN		0x4

//primitive types
typedef unsigned char primitive_type;

#define OBJ_END				0x0
#define OBJ_POINTS			0x1
#define OBJ_LINES			0x2
#define OBJ_FACE			0x3

#define OBJ_INVALID		ULONG_MAX
//define the point structures
struct vertex_position{float x,y,z,w; unsigned char mask;};
struct vertex_texture{float u,v,w; unsigned char mask;};
struct vertex_normal{float i,j,k; unsigned char mask;};
//the point structure contains indices to the relative 3-vectors in the lists
struct vertex
{
unsigned int v;
unsigned int vt;
unsigned int vn;
};

struct primitive
{
vector<vertex> p;		//indices to the point
unsigned char mask;		//mask describing which features (v, vt, vn) are used
primitive_type type;	//the type of primitive (points, lines, face)
};

//axis-aligned bounding box
struct AABB
{
vertex_position min;
vertex_position max;
};

//create variable types
typedef unsigned int OBJint;

//define the OBJ data class
class rtsOBJ
{
private:

/*Current state variables.  These variables are committed to the OBJ object
when a vertex is added.  However, we are careful to only add them once (if they don't
change with every vertex.
*/
vertex_texture current_vt;
vertex_normal current_vn;
bool vt_changed;				//true if a new vt or vn was inserted since the last vertex
bool vn_changed;
unsigned char current_primitive_mask;	//defines what coordinates are being used by the current primitive
//global variable storing the current render mode
OBJint g_CurrentMode;
//output file stream
ofstream g_objFile;
//obj file object
//objData g_OBJ;
//number of vertices since the last BEGIN
unsigned int g_NumVertices;
/*Attribute mask.  This indicates what attributes are stored for each vertex.
Only a single mask applies to each vertex between objBegin() and objEnd().  The
attribute mask is flipped the first time an attribute is set but it is fixed after
the first vertex is passed.*/
/*Attribute reset mask.  This indicates whether or not an attribute has been
reset since the last vertex was rendered.  This applies to OBJ_VT and OBJ_VN*/
//latest texture coordinate sent
unsigned int g_LatestVT;
//latest vertex normal sent
unsigned int g_LatestVN;

//extents of the points in the OBJ file
AABB m_bounds;

OBJint f_InsertPointVertex();
OBJint f_InsertLineVertex();
OBJint f_InsertLineLoopVertex();
OBJint f_InsertLineStripVertex();
OBJint f_InsertTriangleVertex();
OBJint f_InsertTriangleStripVertex();
OBJint f_InsertTriangleFanVertex();
OBJint f_InsertPolygonVertex();
OBJint f_InsertPreviousFaceVertex(unsigned int v_index);
OBJint f_InsertFirstFaceVertex(unsigned int v_index);
OBJint f_InsertNewFaceVertex();
OBJint f_InsertNewLineVertex();
OBJint f_CreateNewFace();
OBJint f_CreateNewLine();
OBJint f_TerminateLineLoop();

//insert coordinate commands
//these are used to handle coordinates of different dimensions (and are called by the associated public function)
OBJint f_InsertVertexf(float x, float y, float z, float w, unsigned char mask);
OBJint f_InsertTexCoordf(float u, float v, float w, unsigned char mask);
OBJint f_InsertNormalf(float i, float j, float k, unsigned char mask);
//output functions
void f_OutputVertices();
void f_OutputPoints();
void f_OutputLines();
void f_OutputFaces();
void f_OutputVertexNormals();
void f_OutputTextureCoordinates();
void f_ClearAll();

//methods for reading from a file

public:
/*These vectors store the vertex and primitive data from the obj file.
All vertices, texture coordinates, and normals are stored in m_v, m_vt, m_vn
respectively.  The vectors for each primitive store an index into m_v, m_vt,
and m_vn identifying the associated coordinate.  Basically, the data is stored
in a structure very similar to the OBJ file itself.
*/
vector<vertex_position> v_list;
vector<vertex_texture> vt_list;
vector<vertex_normal> vn_list;
vector<primitive> primitives;

vector<unsigned int> points;
vector<unsigned int> lines;
vector<unsigned int> faces;

public:

OBJint objOpen(const char* filename);
OBJint objBegin(OBJint obj_mode);
OBJint objEnd();
OBJint objClose();
OBJint objNormal3f(float i, float j, float k);
OBJint objNormal2f(float i, float j);
OBJint objNormal1f(float i);
OBJint objTexCoord3f(float u, float v, float w);
OBJint objTexCoord2f(float u, float v);
OBJint objTexCoord1f(float u);
OBJint objVertex1f(float x);
OBJint objVertex2f(float x, float y);
OBJint objVertex3f(float x, float y, float z);
OBJint objVertex4f(float x, float y, float z, float w);
OBJint SaveFile(const char* filename);

//direct insertion methods
void insertVertexPosition(float x, float y, float z, unsigned char mask = OBJ_V_X | OBJ_V_Y | OBJ_V_Z);
void insertLine(unsigned int num_points, unsigned int* pointlist, unsigned int* normallist, unsigned int* texturelist);

//get methods
unsigned int getNumVertices();
unsigned int getNumLines();
unsigned int getNumFaces();
unsigned int getNumPointLists();
unsigned int getNumTexCoords();
unsigned int getNumNormals();

//these functions return the coordinate index as well as the value
unsigned int getNumFaceVertices(unsigned int face);
unsigned int getFaceVertex(unsigned int face, unsigned int vertex);
unsigned int getFaceNormal(unsigned int face, unsigned int normal);
unsigned int getFaceTexCoord(unsigned int face, unsigned int texcoord);
unsigned int getNumLineVertices(unsigned int line);
unsigned int getLineVertex(unsigned int line, unsigned int vertex);
unsigned int getLineTexCoord(unsigned int line, unsigned int texcoord);
point3D<float> getVertex3d(unsigned int index);
point3D<float> getTexCoord3d(unsigned int index);
point3D<float> getNormal3d(unsigned int index);
vertex_position getVertex(unsigned int index);
vertex_texture getTexCoord(unsigned int index);
vertex_normal getNormal(unsigned int index);
AABB getBoundingBox(){return m_bounds;}
void Scale(float scale_x, float scale_y, float scale_z);
void Translate(float trans_x, float trans_y, float trans_z);

float GetDistance(float x, float y, float z);

unsigned int getPrimitiveType(unsigned int primitive);

//constructors
rtsOBJ();
void CopyOBJ(const rtsOBJ& obj);
//assignment
rtsOBJ& operator=(const rtsOBJ& obj)
{
CopyOBJ(obj);
return *this;
}
//copy
rtsOBJ(const rtsOBJ& obj)
{
CopyOBJ(obj);
//return *this;
}

//Iterator stuff
class iterator
{
friend class rtsOBJ;
private:
rtsOBJ* obj;
bool end_primitive;
bool end_object;
unsigned int primitive_index;
public:
unsigned int operator*();
void operator++();
bool operator==(rtsOBJ::iterator operand);
bool operator!=(rtsOBJ::iterator operand);
unsigned int size(){return obj->primitives[primitive_index].p.size();};
void print();
};

iterator begin();
iterator end();
};

//define error codes
#define OBJ_OK				0x0100
#define OBJ_ERROR			0x0101

//define the different modes
#define OBJ_NONE			0x0
#define OBJ_POINTS			0x1
#define OBJ_LINES			0x2
#define OBJ_LINE_STRIP		0x3
#define OBJ_LINE_LOOP		0x4
#define OBJ_TRIANGLES		0x5
#define OBJ_TRIANGLE_STRIP	0x6
#define OBJ_TRIANGLE_FAN	0x7
#define OBJ_POLYGON			0x10

//initialize the OBJ file
OBJint objOpen(char* filename);
//close the obj file
OBJint objClose();

//start rendering in a certain mode
OBJint objBegin(OBJint mode);
//stop the current rendering sequence
OBJint objEnd(void);
//render a vertex to the file
OBJint objVertex1f(float x);
OBJint objVertex2f(float x, float y);
OBJint objVertex3f(float x, float y, float z);
OBJint objVertex4f(float x, float y, float z, float w);
//set a normal vector for a vertex
OBJint objNormal3f(float i, float j, float k);
OBJint objNormal2f(float i, float j);
OBJint objNormal1f(float i);
//set a texture coordinate for a vertex
OBJint objTexCoord3f(float u, float v, float w);
OBJint objTexCoord2f(float u, float v);
OBJint objTexCoord1f(float u);

//global variable for global functions
extern rtsOBJ g_OBJ;

/*BUG NOTES
The standard function calls for inserting vertices don't work anymore.  I've fixed points
but everything beyond that has to be updated.
*/

#include "objJedi.h"
#include "rtsVector3d.h"
#include "rtsPoint3d.h"
#include <string>
//variable for use in global functions
rtsOBJ g_OBJ;

/********UTILITY METHODS*******************************/
void rtsOBJ::Scale(float scale_x, float scale_y, float scale_z)
{
vector<vertex_position>::iterator i;
for(i = v_list.begin(); i!= v_list.end(); i++)
{
(*i).x *= scale_x;
(*i).y *= scale_y;
(*i).z *= scale_z;
}
}

void rtsOBJ::Translate(float trans_x, float trans_y, float trans_z)
{
vector<vertex_position>::iterator i;
for(i = v_list.begin(); i!= v_list.end(); i++)
{
(*i).x += trans_x;
(*i).y += trans_y;
(*i).z += trans_z;
}

}

float rtsOBJ::GetDistance(float x, float y, float z)
{
//gets the distance between the specified point and the nearest surface of the OBJ
//currently only works for lines

//cout<<"Primitives: "<<primitives.size()<<endl;
unsigned int num_primitives = primitives.size();
point3D<float> p0, p1, p2;
float min_dist = 255;
float dist;
unsigned int p, l;
vector3D<float> v, w;
float c1, c2, b;
point3D<float> Pb;

//for each line
for(l=0; l<num_primitives; l++)
{
if(primitives[l].type & OBJ_LINES)
{
//for each point
for(p = 1; p<primitives[l].p.size(); p++)
{

vertex_position v1 = v_list[primitives[l].p[p-1].v];
vertex_position v2 = v_list[primitives[l].p[p].v];
p1.x = v1.x;
p1.y = v1.y;
p1.z = v1.z;
p2.x = v2.x;
p2.y = v2.y;
p2.z = v2.z;

p0.x = x;
p0.y = y;
p0.z = z;

v = p2 - p1;
w = p0 - p1;
if((c1 = w*v) <= 0)
dist = (p1 - p0).Length();
else if((c2 = v*v) <= c1)
dist = (p2 - p0).Length();
else
{
b = c1/c2;
Pb = p1 + b*v;
dist = (Pb - p0).Length();
}
if(dist < min_dist)
min_dist = dist;

}
}
}
//cout<<"---------------------------------------------"<<endl;

return min_dist;

}

/********CLASS METHOD DEFINITIONS**********************/
//constructors

//constructor
rtsOBJ::rtsOBJ()
{
g_CurrentMode = OBJ_NONE;
g_NumVertices = 0;
g_LatestVT = 0;
g_LatestVN = 0;

m_bounds.min.x = m_bounds.min.y = m_bounds.min.z = 99999;
m_bounds.max.x = m_bounds.max.y = m_bounds.max.z = -99999;

}

void rtsOBJ::CopyOBJ(const rtsOBJ& obj)
{
current_vt = obj.current_vt;
current_vn = obj.current_vn;
vt_changed = obj.vt_changed;				//true if a new vt or vn was inserted since the last vertex
vn_changed = obj.vn_changed;
current_primitive_mask = obj.current_primitive_mask;	//defines what coordinates are being used by the current primitive
//global variable storing the current render mode
g_CurrentMode = obj.g_CurrentMode;
//output file stream
//g_objFile = obj.g_objFile;
//obj file object
//objData g_OBJ;
//number of vertices since the last BEGIN
g_NumVertices = obj.g_NumVertices;
/*Attribute mask.  This indicates what attributes are stored for each vertex.
Only a single mask applies to each vertex between objBegin() and objEnd().  The
attribute mask is flipped the first time an attribute is set but it is fixed after
the first vertex is passed.*/
/*Attribute reset mask.  This indicates whether or not an attribute has been
reset since the last vertex was rendered.  This applies to OBJ_VT and OBJ_VN*/
//latest texture coordinate sent
g_LatestVT = obj.g_LatestVT;
//latest vertex normal sent
g_LatestVN = obj.g_LatestVN;
m_bounds = obj.m_bounds;

v_list = obj.v_list;
vt_list = obj.vt_list;
vn_list = obj.vn_list;
primitives = obj.primitives;

points = obj.points;
lines = obj.lines;
faces = obj.faces;
}

//opens an obj file for rendering
OBJint rtsOBJ::objOpen(const char* filename)
{
g_objFile.open(filename);
return OBJ_OK;
}

//close the obj file
OBJint rtsOBJ::objClose()
{
//TODO: write obj data
f_OutputVertices();
f_OutputTextureCoordinates();
f_OutputVertexNormals();
f_OutputPoints();
f_OutputLines();
f_OutputFaces();

//close the file
g_objFile.close();

//delete all of the data from the global object
f_ClearAll();
return OBJ_OK;
}

OBJint rtsOBJ::objBegin(OBJint mode)
{
//make sure that we aren't currently rendering
if(g_CurrentMode != OBJ_NONE)
return OBJ_ERROR;
//make sure that the given mode is valid
if(mode < OBJ_POINTS || mode > OBJ_POLYGON)
return OBJ_ERROR;

//otherwise, go ahead and set the mode
g_CurrentMode = mode;
//set the number of vertices to zero
g_NumVertices = 0;

//reset the current state primitive state

return OBJ_OK;
}

OBJint rtsOBJ::objEnd()
{
OBJint error = OBJ_OK;
//check to make sure the number of rendered vertices is valid for the current mode
switch(g_CurrentMode)
{
case OBJ_NONE:
//can't quit if we haven't started
error = OBJ_ERROR;
break;
case OBJ_LINES:
//if less than two vertices or an odd number of vertices
if(g_NumVertices < 2 || g_NumVertices%2 != 0)
{
//if there wasn't a vertex at all
if(g_NumVertices == 0)
error = OBJ_ERROR;
//if there was at least one vertex
else
{
//pop the last line off the list
primitives.pop_back();
lines.pop_back();
error = OBJ_ERROR;
}
}
break;
case OBJ_LINE_STRIP:
//if less than two vertices
if(g_NumVertices < 2)
{
//if there wasn't a vertex at all
if(g_NumVertices == 0)
error = OBJ_ERROR;
//if there was at least one vertex
else
{
//pop the last line off the list
primitives.pop_back();
lines.pop_back();
error = OBJ_ERROR;
}
}
break;
case OBJ_LINE_LOOP:
//if less than three vertices
if(g_NumVertices < 3)
{
//pop the last line off the list
primitives.pop_back();
lines.pop_back();
error = OBJ_ERROR;
}
//connect the first and last points
else
{
error = f_TerminateLineLoop();
}
break;
case OBJ_TRIANGLES:
//if less than three vertices or not a power of three
if(g_NumVertices < 3 || g_NumVertices%3 !=0)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
case OBJ_TRIANGLE_STRIP:
//if less than three vertices
if(g_NumVertices < 3)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
case OBJ_TRIANGLE_FAN:
//if less than three vertices
if(g_NumVertices < 3)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
if(g_NumVertices < 4 || g_NumVertices%4 != 0)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
//has to be at least 4 vertices and an even number
if(g_NumVertices < 4 || g_NumVertices%2 != 0)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
case OBJ_POLYGON:
//has to be at least three vertices
if(g_NumVertices < 3)
{
primitives.pop_back();
faces.pop_back();
error = OBJ_ERROR;
}
break;
}

//just for closure, reset the attribute reset mask
//stop rendering
g_CurrentMode = OBJ_NONE;
return error;
}

OBJint rtsOBJ::f_InsertVertexf(float x, float y, float z, float w, unsigned char mask)
{
//make sure we're rendering
if(g_CurrentMode == OBJ_NONE)
return OBJ_ERROR;

//insert the vertex into the vertex vector
vertex_position v;
v_list.push_back(v);
//insert texture coordinate and normal if specified for this primitive
vt_list.push_back(current_vt);
vn_list.push_back(current_vn);

//increment the number of vertices inserted
g_NumVertices++;

//handle each case of the vertex creation individually
OBJint error = OBJ_OK;
switch(g_CurrentMode)
{
case OBJ_POINTS:
error = f_InsertPointVertex();
break;
case OBJ_LINES:
error = f_InsertLineVertex();
break;
case OBJ_LINE_LOOP:
error = f_InsertLineLoopVertex();
break;
case OBJ_LINE_STRIP:
error = f_InsertLineStripVertex();
break;
case OBJ_TRIANGLES:
error = f_InsertTriangleVertex();
break;
case OBJ_TRIANGLE_STRIP:
error = f_InsertTriangleStripVertex();
break;
case OBJ_TRIANGLE_FAN:
error = f_InsertTriangleFanVertex();
break;
break;
break;
case OBJ_POLYGON:
error = f_InsertPolygonVertex();
break;
}
//set the reset mask to zero

//set the attribute mask to include vertex position

//return the result of the insertion
return error;
}

OBJint rtsOBJ::objVertex1f(float x)
{
return f_InsertVertexf(x, 0.0, 0.0, 0.0, OBJ_V_X);
}

OBJint rtsOBJ::objVertex2f(float x, float y)
{
return f_InsertVertexf(x, y, 0.0, 0.0, OBJ_V_X | OBJ_V_Y);
}

OBJint rtsOBJ::objVertex3f(float x, float y, float z)
{
return f_InsertVertexf(x, y, z, 0.0, OBJ_V_X | OBJ_V_Y | OBJ_V_Z);

}

OBJint rtsOBJ::objVertex4f(float x, float y, float z, float w)
{
return f_InsertVertexf(x, y, z, w, OBJ_V_X | OBJ_V_Y | OBJ_V_Z | OBJ_V_W);
}

OBJint rtsOBJ::objNormal3f(float i, float j, float k)
{
return f_InsertNormalf(i, j, k, OBJ_VN_I | OBJ_VN_J | OBJ_VN_K);
}
OBJint rtsOBJ::objNormal2f(float i, float j)
{
return f_InsertNormalf(i, j, 0.0, OBJ_VN_I | OBJ_VN_J);
}

OBJint rtsOBJ::objNormal1f(float i)
{
return f_InsertNormalf(i, 0.0, 0.0, OBJ_VN_I);
}

OBJint rtsOBJ::f_InsertNormalf(float i, float j, float k, unsigned char mask)
{
/*DEPRECATED
//if the mode is not rendering faces, there is an error
if(g_CurrentMode < OBJ_TRIANGLES)
return OBJ_ERROR;
//if the normal attribute flag is not set, set it (as long as a vertex hasn't been written)
{
//if a vertex has been rendered, then we can't change the attribute, so exit
if(g_NumVertices > 0)
return OBJ_ERROR;
else
//otherwise, just set the attribute flag
}

//insert the new normal into the normal list for the file
vertex_normal new_vn;
new_vn.i = i;  new_vn.j = j;  new_vn.k = k;
vn_list.push_back(new_vn);
*/
current_vn.i = i;											//set the current texture state to the given coordinates
current_vn.j = j;
current_vn.k = k;
vn_changed = true;											//the texture coordinate state has changed
current_primitive_mask = current_primitive_mask | OBJ_VN;	//the current primitive now uses texture coordinates (if it didn't previously)

return OBJ_OK;
}

OBJint rtsOBJ::objTexCoord3f(float u, float v, float w)
{
return f_InsertTexCoordf(u, v, w, OBJ_VT_U | OBJ_VT_V | OBJ_VT_W);
}

OBJint rtsOBJ::objTexCoord2f(float u, float v)
{
return f_InsertTexCoordf(u, v, 0.0, OBJ_VT_U | OBJ_VT_V);
}

OBJint rtsOBJ::objTexCoord1f(float u)
{
return f_InsertTexCoordf(u, 0.0, 0.0, OBJ_VT_U);
}

OBJint rtsOBJ::f_InsertTexCoordf(float u, float v, float w, unsigned char mask)
{
/*
DEPRECATED
//if the normal attribute flag is not set, set it (as long as a vertex hasn't been written)
{
//if a vertex has been rendered, then we can't change the attribute, so exit
if(g_NumVertices > 0)
return OBJ_ERROR;
else
//otherwise, just set the attribute flag
}

//insert the new texture coordinate into the list for the file
vertex_texture new_vt;
new_vt.u = u;  new_vt.v = v;  new_vt.w = w;
vt_list.push_back(new_vt);
*/
current_vt.u = u;											//set the current texture state to the given coordinates
current_vt.v = v;
current_vt.w = w;
vt_changed = true;											//the texture coordinate state has changed
current_primitive_mask = current_primitive_mask | OBJ_VT;	//the current primitive now uses texture coordinates (if it didn't previously)

return OBJ_OK;
}

void rtsOBJ::insertVertexPosition(float x, float y, float z, unsigned char mask)
{
vertex_position v;
v.x = x;
v.y = y;
v.z = z;

v_list.push_back(v);
}

void rtsOBJ::insertLine(unsigned int num_points, unsigned int* pointlist, unsigned int* normallist, unsigned int* texturelist)
{
//create the new primitive
primitive line;
line.type = OBJ_LINES;

//set the mask based on the passed data
if(pointlist != NULL)
if(normallist != NULL)
if(texturelist != NULL)

//insert the line
unsigned int v;
vertex new_vert;
for(v=0; v<num_points; v++)
{
if(pointlist)
new_vert.v = pointlist[v];
if(normallist)
new_vert.vn = normallist[v];
if(texturelist)
new_vert.vt = texturelist[v];
line.p.push_back(new_vert);
}
//insert the new primitive into the list
primitives.push_back(line);
}
OBJint rtsOBJ::f_InsertPointVertex()
{
//insert the most recent point into the most recent point list
//if this is the first vertex, create the point list
if(g_NumVertices == 1)
{
primitive new_p;						//create the new point
new_p.type = OBJ_POINTS;
points.push_back(primitives.size());	//store the primitive id in the points list
primitives.push_back(new_p);
}

//find the id of the most recent primitive
unsigned int p_index = primitives.size() - 1;
//find the index of the recent point
vertex p;
p.v = v_list.size() - 1;
//insert the indices for texture coordinates and normal if necessary
p.vt = vt_list.size() - 1;
p.vn = vn_list.size() - 1;

//insert the vertex index into the primitive's point list
primitives[p_index].p.push_back(p);

//push the point into the points list if it is not the only point in the primitive
if(g_NumVertices > 1)
points.push_back(p_index);

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertLineVertex()
{
//if this is an odd vertex, create a new line
if(g_NumVertices%2 == 1)
{
f_CreateNewLine();
}

f_InsertNewLineVertex();

return OBJ_OK;
}
OBJint rtsOBJ::f_InsertLineLoopVertex()
{
//technically, this is the same as inserting a line strip vertex
f_InsertLineStripVertex();
return OBJ_OK;
}

OBJint rtsOBJ::f_InsertLineStripVertex()
{
if(g_NumVertices == 1)
{
f_CreateNewLine();
}

f_InsertNewLineVertex();

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertTriangleVertex()
{
//if this is the first vertex in a triangle, create a new triangle
if(g_NumVertices%3 == 1)
{
f_CreateNewFace();
}

f_InsertNewFaceVertex();

return OBJ_OK;
}
OBJint rtsOBJ::f_InsertTriangleStripVertex()
{
//in the special case of the first three vertices, just create a triangle
if(g_NumVertices < 4)
f_InsertTriangleVertex();
else
{

//create a new face for the new triangle
f_CreateNewFace();

//make sure to get the ordering right:
//		Even vertices provide the previous two vertices "in order": (v2, v3, v4)
//		Odd vertices provide the previous two vertices in reverse order: (v1, v3, v2)
if(g_NumVertices % 2 == 1)
{
f_InsertPreviousFaceVertex(2);
f_InsertPreviousFaceVertex(1);
f_InsertNewFaceVertex();
}
else
{
f_InsertPreviousFaceVertex(1);
f_InsertNewFaceVertex();
f_InsertPreviousFaceVertex(2);
}
}

return OBJ_OK;
}
OBJint rtsOBJ::f_InsertTriangleFanVertex()
{
//in the special case of the first three vertices, just create a triangle
if(g_NumVertices <4)
f_InsertTriangleVertex();
else
{
//create a new face for the new triangle
f_CreateNewFace();
//add the previous vertices to the face
f_InsertFirstFaceVertex(0);
f_InsertPreviousFaceVertex(2);
//insert the current vertex
f_InsertNewFaceVertex();
}

return OBJ_OK;
}
{
//if this is the first vertex in a quad, create a new quad
if(g_NumVertices%4 == 1)
{
f_CreateNewFace();
}

f_InsertNewFaceVertex();

return OBJ_OK;
}
{
//in the case of one of the first four vertices, just create a quad
if(g_NumVertices < 5)
//if the vertex is odd (it will be the third vertex of a quad)
else if(g_NumVertices%2 == 1)
{
//create a new face for the new quad
f_CreateNewFace();
f_InsertPreviousFaceVertex(2);
f_InsertPreviousFaceVertex(3);
f_InsertNewFaceVertex();
}
else
{
//if this is the last vertex of the quad, just add it
f_InsertNewFaceVertex();

}
return OBJ_OK;
}
OBJint rtsOBJ::f_InsertPolygonVertex()
{
//if this is the first vertex, create the quad
if(g_NumVertices == 1)
{
f_CreateNewFace();
}
f_InsertNewFaceVertex();

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertPreviousFaceVertex(unsigned int v_index)
{
/*Finds the vertex used in the previous face with the given index and
inserts it into the current face.  This limits the redundancy in the file
for re-used vertices (in strips and fans).  This also transfers texture
and normal information.*/

//find the index of the previous face
unsigned int prev_f_index = primitives.size() - 2;
//find the index of the current face
unsigned int f_index = prev_f_index +1;
//add the vertex information from the previous face to this face
primitives[f_index].p.push_back(primitives[prev_f_index].p[v_index]);

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertFirstFaceVertex(unsigned int v_index)
{
/*Finds the vertex used in the first face (since the last objBegin())
with the given index and inserts it at the end of the current face.
This includes texture and normal information.*/

//The result depends on the type of face being rendered
//So far, this function only applies to triangle fans
if(g_CurrentMode != OBJ_TRIANGLE_FAN)
return OBJ_ERROR;

//calculate the number of faces that have been rendered
unsigned int num_faces = g_NumVertices - 2;
//find the index of the first face
unsigned int first_f_index = primitives.size() - num_faces;
//find the index of the current face
unsigned int f_index = primitives.size() - 1;
//transfer the vertex information from the first face to this one
primitives[f_index].p.push_back(primitives[first_f_index].p[v_index]);

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertNewFaceVertex()
{
/*This inserts information about the current vertex into the current face*/
//find the new vertex index
vertex p;
p.v = v_list.size() -1;
p.vt = vt_list.size() - 1;
p.vn = vn_list.size() -1;
//find the face index
unsigned int f_index = primitives.size() -1;
//INSERT VERTEX AND ATTRIBUTE DATA
//just add the vertex to the face
primitives[f_index].p.push_back(p);

return OBJ_OK;
}

OBJint rtsOBJ::f_InsertNewLineVertex()
{
/*This inserts information about the current vertex into the current line*/
//find the new vertex index
vertex p;
p.v = v_list.size() -1;
p.vt = vt_list.size() - 1;
//find the line index
unsigned int l_index = primitives.size() -1;

//add the vertex to the line
primitives[l_index].p.push_back(p);

return OBJ_OK;
}

OBJint rtsOBJ::f_CreateNewFace()
{
primitive new_f;
new_f.type = OBJ_FACE;
faces.push_back(primitives.size());
primitives.push_back(new_f);

return OBJ_OK;
}

OBJint rtsOBJ::f_CreateNewLine()
{
primitive new_l;
new_l.type = OBJ_LINES;
lines.push_back(primitives.size());
primitives.push_back(new_l);

return OBJ_OK;
}

OBJint rtsOBJ::f_TerminateLineLoop()
{
/*This function just terminates the line loop by setting the last vertex
to the first vertex.*/
if(g_CurrentMode != OBJ_LINE_LOOP)
return OBJ_ERROR;
//find the index for the current line
unsigned int l_index = lines.size() -1;
//insert the first vertex as the last vertex
primitives[l_index].p.push_back(primitives[l_index].p[0]);

return OBJ_OK;
}

void rtsOBJ::f_OutputVertices()
{
//get the number of vertices in the object
unsigned int v_num = v_list.size();
for(unsigned int i=0; i<v_num; i++)
{
g_objFile<<"v";
g_objFile<<" "<<v_list[i].x;
g_objFile<<" "<<v_list[i].y;
g_objFile<<" "<<v_list[i].z;
g_objFile<<" "<<v_list[i].w;
g_objFile<<endl;
}

}

void rtsOBJ::f_OutputVertexNormals()
{
//get the number of normals in the object
unsigned int vn_num = vn_list.size();
for(unsigned int i=0; i<vn_num; i++)
{
g_objFile<<"vn ";
{
g_objFile<<vn_list[i].i;
{
g_objFile<<" "<<vn_list[i].j;
g_objFile<<" "<<vn_list[i].k;
}
}
g_objFile<<endl;
}
}

void rtsOBJ::f_OutputTextureCoordinates()
{
//get the number of vertices in the object
unsigned int vt_num = vt_list.size();
for(unsigned int i=0; i<vt_num; i++)
{
g_objFile<<"vt ";
{
g_objFile<<vt_list[i].u;
{
g_objFile<<" "<<vt_list[i].v;
g_objFile<<" "<<vt_list[i].w;
}
}
g_objFile<<endl;
}
}

void rtsOBJ::f_OutputPoints()
{
/*
//get the number of point vectors
unsigned int p_num = points.size();

//for each point vector, output the points
for(unsigned int i=0; i<p_num; i++)
{
g_objFile<<"p";
unsigned int v_num = points[i].p.size();
for(unsigned int j=0; j<v_num; j++)
{
g_objFile<<" "<<points[i].p[j].v+1;
}
g_objFile<<endl;
}
*/
}

void rtsOBJ::f_OutputLines()
{
/*
//get the number of line vectors
unsigned int l_num = lines.size();

//for each line vector, output the associated points
for(unsigned int i=0; i<l_num; i++)
{
g_objFile<<"l";
unsigned int v_num = lines[i].p.size();
for(unsigned int j=0; j<v_num; j++)
{
g_objFile<<" "<<lines[i].p[j].v+1;
//output texture coordinate if there are any
g_objFile<<"/"<<lines[i].p[j].vt+1;
}
g_objFile<<endl;
}
*/
}

void rtsOBJ::f_OutputFaces()
{
/*
//get the number of faces
unsigned int f_num = faces.size();

//for each face, output the associated points
for(unsigned int i=0; i<f_num; i++)
{
g_objFile<<"f";
//get the number of face vertices
unsigned int v_num = faces[i].p.size();
for(unsigned int j=0; j<v_num; j++)
{
g_objFile<<" "<<faces[i].p[j].v+1;
//if there are texture coordinates
g_objFile<<"/"<<faces[i].p[j].vt+1;
//if there is a normal
{
//but no texture coordinates, put an extra slash
g_objFile<<"/";
g_objFile<<"/"<<faces[i].p[j].vn+1;
}

}
g_objFile<<endl;
}
*/
}

void rtsOBJ::f_ClearAll()
{
rtsOBJ();
//clear all data from the global obj function
faces.clear();
lines.clear();
points.clear();
v_list.clear();
vn_list.clear();
vt_list.clear();
primitives.clear();
}

/*GLOBAL FUNCTION DEFINITIONS*/
//initialize the OBJ file
OBJint objOpen(char* filename)
{
return g_OBJ.objOpen(filename);
}
//close the obj file
OBJint objClose()
{
return g_OBJ.objClose();
}

//start rendering in a certain mode
OBJint objBegin(OBJint mode)
{
return g_OBJ.objBegin(mode);
}
//stop the current rendering sequence
OBJint objEnd(void)
{
return g_OBJ.objEnd();
}
//render a vertex to the file
OBJint objVertex1f(float x)
{
return g_OBJ.objVertex1f(x);
}

OBJint objVertex2f(float x, float y)
{
return g_OBJ.objVertex2f(x, y);
}

OBJint objVertex3f(float x, float y, float z)
{
return g_OBJ.objVertex3f(x, y, z);
}

OBJint objVertex4f(float x, float y, float z, float w)
{
return g_OBJ.objVertex4f(x, y, z, w);
}
//set a normal vector for a vertex
OBJint objNormal3f(float i, float j, float k)
{
return g_OBJ.objNormal3f(i, j, k);
}
OBJint objNormal2f(float i, float j)
{
return g_OBJ.objNormal2f(i, j);
}
OBJint objNormal1f(float i)
{
return g_OBJ.objNormal1f(i);
}
//set a texture coordinate for a vertex
OBJint objTexCoord3f(float u, float v, float w)
{
return g_OBJ.objTexCoord3f(u, v, w);
}

OBJint objTexCoord2f(float u, float v)
{
return g_OBJ.objTexCoord2f(u,v);
}

OBJint objTexCoord1f(float u)
{
return g_OBJ.objTexCoord1f(u);
}

{
vertex_position new_vertex;
//get each coordinate
infile>>new_vertex.x;

if(infile.peek() == ' ')
{
infile>>new_vertex.y;
}
if(infile.peek() == ' ')
{
infile>>new_vertex.z;
}
if(infile.peek() == ' ')
{
infile>>new_vertex.w;
}
int c = infile.peek();
//ignore the rest of the line
infile.ignore(1000, '\n');
c = infile.peek();

//insert the vertex into the list
v_list.push_back(new_vertex);

//adjust the extents of the model

return OBJ_OK;
}

{
vertex_normal new_normal;
infile>>new_normal.i;
//get every other component
if(infile.peek() == ' ')
{
infile>>new_normal.j;
}
if(infile.peek() == ' ')
{
infile>>new_normal.k;
}
//ignore the rest of the line
infile.ignore(1000, '\n');
//insert the normal
vn_list.push_back(new_normal);

return OBJ_OK;
}

{
vertex_texture new_texcoord;
infile>>new_texcoord.u;
//get every other component
if(infile.peek() == ' ')
{
infile>>new_texcoord.v;
}
if(infile.peek() == ' ')
{
infile>>new_texcoord.w;
}

//ignore the rest of the line
infile.ignore(1000, '\n');
//insert the texture coordinate
vt_list.push_back(new_texcoord);

return OBJ_OK;

}

{
//store the line vertex
infile>>new_point.v;
new_point.v--;
//new_face.v.push_back(new_v - 1);
if(infile.peek() == '/')
{
infile.get();
//if there actually is a texcoord
if(infile.peek() != '/')
{
infile>>new_point.vt;						//get the index
new_point.vt--;
//new_face.vt.push_back(new_vt - 1);
}
}
//check for a normal
if(infile.peek() == '/')
{
infile.get();
infile>>new_point.vn;							//get the index
new_point.vn--;
}

return OBJ_OK;
}

{
//create a new point list
primitive new_primitive;
new_primitive.type = type;
//until the end of the line
while(infile.peek() != '\n')
{
if(infile.peek() == ' ')
infile.get();
else
{
vertex new_point;
new_primitive.p.push_back(new_point);
}
}
//ignore the rest of the line
infile.ignore(1000, '\n');

//push the id of the primitive into the new list
//:DEBUG:
if(type == OBJ_POINTS)
points.push_back(primitives.size());
else if(type == OBJ_LINES)
lines.push_back(primitives.size());
else if(type == OBJ_FACE)
faces.push_back(primitives.size());

primitives.push_back(new_primitive);	//push the new primitive

return OBJ_OK;
}

{
if(v.x < m_bounds.min.x)
m_bounds.min.x = v.x;
if(v.y < m_bounds.min.y)
m_bounds.min.y = v.y;
if(v.z < m_bounds.min.z)
m_bounds.min.z = v.z;

if(v.x > m_bounds.max.x) m_bounds.max.x = v.x;
if(v.y > m_bounds.max.y) m_bounds.max.y = v.y;
if(v.z > m_bounds.max.z) m_bounds.max.z = v.z;

return OBJ_OK;
}

{
f_ClearAll();
//open the file as a stream
ifstream infile;
infile.open(filename);
if(!infile)
exit(1);

unsigned int vertices = 0;

string token;
infile>>token;
while(!infile.eof())
{
//if the token is some vertex property
if(token == "v")
else if(token == "vn")
else if(token == "vt")
else if(token == "p")
else if(token == "l")
else if(token == "f")
else
infile.ignore(9999, '\n');
//vertices++;

infile>>token;
}

}

{
f_ClearAll();
//open the file as a stream
ifstream infile;
infile.open(filename);
if(!infile.is_open())
return OBJ_ERROR;

vector<vertex_position> swcVertices;
float token;
objBegin(OBJ_LINES);
while(!infile.eof())
{
vertex_position v;
infile>>token;			//get the id
infile>>token;			//get the fiber type
infile>>v.x;			//get the node position
infile>>v.y;
infile>>v.z;
infile>>token;			//get the parent

//insert the node into the swc vector
swcVertices.push_back(v);
//now draw the line from the parent to the current node
if(token != -1)
{
unsigned int i = (unsigned int)token - 1;
objVertex3f(swcVertices[i].x, swcVertices[i].y, swcVertices[i].z);
objVertex3f(v.x, v.y, v.z);
}
}
objEnd();

return OBJ_OK;

}
{
string strFilename = filename;
int length = strFilename.length();
string extension = strFilename.substr(strFilename.length() - 3, 3);
if(!extension.compare(string("obj")))
else if(!extension.compare(string("swc")))

}

OBJint rtsOBJ::SaveFile(const char* filename)
{
//open the file as a stream
ofstream outfile;
outfile.open(filename);
if(!outfile.is_open())
return OBJ_ERROR;

//output vertex positions
vector<vertex_position>::iterator v;
for(v=v_list.begin(); v!= v_list.end(); v++)
{
outfile<<"v";
{
outfile<<' '<<(*v).x;
{
outfile<<' '<<(*v).y;
{
outfile<<' '<<(*v).z;
outfile<<' '<<(*v).w;
}
}
}
outfile<<'\n';
}

//output vertex texture coordinates
vector<vertex_texture>::iterator vt;
for(vt=vt_list.begin(); vt!= vt_list.end(); vt++)
{
outfile<<"vt";
{
outfile<<' '<<(*vt).u;
{
outfile<<' '<<(*vt).v;
outfile<<' '<<(*vt).w;
}
}
outfile<<'\n';
}

//output vertex normal coordinates
vector<vertex_normal>::iterator vn;
for(vn=vn_list.begin(); vn!= vn_list.end(); vn++)
{
outfile<<"vn";
{
outfile<<' '<<(*vn).i;
{
outfile<<' '<<(*vn).j;
outfile<<' '<<(*vn).k;
}
}
outfile<<'\n';
}

//output each primitive
vector<primitive>::iterator p;
vector<vertex>::iterator vert;
for(p=primitives.begin(); p!= primitives.end(); p++)
{
switch((*p).type)
{
case OBJ_POINTS:
outfile<<"p";			//output the points token
break;
case OBJ_LINES:
outfile<<"l";			//output the lines token
break;
case OBJ_FACE:
outfile<<"f";			//output the face token
break;
}

//for each vertex in the list for the primitive
for(vert = (*p).p.begin(); vert != (*p).p.end(); vert++)
{
outfile<<' '<<(*vert).v + 1;
outfile<<'/'<<(*vert).vt + 1;
{
outfile<<'/';
outfile<<'/'<<(*vert).vn + 1;
}
}
outfile<<'\n';

}

}

//get methods
unsigned int rtsOBJ::getNumVertices(){return v_list.size();}
unsigned int rtsOBJ::getNumLines(){return lines.size();}
unsigned int rtsOBJ::getNumFaces(){return faces.size();}
unsigned int rtsOBJ::getNumPointLists(){return points.size();}
unsigned int rtsOBJ::getNumTexCoords(){return vt_list.size();}
unsigned int rtsOBJ::getNumNormals(){return vn_list.size();}

//these functions return the coordinate index as well as the value
unsigned int rtsOBJ::getNumFaceVertices(unsigned int face){return primitives[face].p.size();}
unsigned int rtsOBJ::getFaceVertex(unsigned int face, unsigned int vertex){return primitives[face].p[vertex].v;}
unsigned int rtsOBJ::getFaceNormal(unsigned int face, unsigned int normal){return primitives[face].p[normal].vn;}
unsigned int rtsOBJ::getFaceTexCoord(unsigned int face, unsigned int texcoord){return primitives[face].p[texcoord].vt;}
unsigned int rtsOBJ::getNumLineVertices(unsigned int line){return primitives[line].p.size();}
unsigned int rtsOBJ::getLineVertex(unsigned int line, unsigned int vertex){return primitives[line].p[vertex].v;}
unsigned int rtsOBJ::getLineTexCoord(unsigned int line, unsigned int texcoord){return primitives[line].p[texcoord].vt;}
point3D<float> rtsOBJ::getVertex3d(unsigned int index)
{
return point3D<float>(v_list[index].x, v_list[index].y, v_list[index].z);
}
point3D<float> rtsOBJ::getTexCoord3d(unsigned int index)
{
return point3D<float>(vt_list[index].u, vt_list[index].v, vt_list[index].w);
}
point3D<float> rtsOBJ::getNormal3d(unsigned int index)
{
return point3D<float>(vn_list[index].i, vn_list[index].j, vn_list[index].k);
}
vertex_position rtsOBJ::getVertex(unsigned int index){return v_list[index];}
vertex_texture rtsOBJ::getTexCoord(unsigned int index){return vt_list[index];}
vertex_normal rtsOBJ::getNormal(unsigned int index){return vn_list[index];}

unsigned int rtsOBJ::getPrimitiveType(unsigned int primitive)
{
/*
switch(primitives[i].type)
{
case OBJ_POINTS:
return OBJ_POINTS;
break;
case OBJ_LINES:
return OBJ_LINES;
break;
case OBJ_FACE:
f_RenderFace(i);
break;
}*/
return 0;
}
/****************************************************/
/*******Iterator Methods*****************************/
/****************************************************/

rtsOBJ::iterator rtsOBJ::begin()
{
//create an iterator that will be returned and assign it to this OBJ
iterator result;
result.obj = this;
result.end_object = false;
result.end_primitive = false;

//if there are no primitives, return the end iterator
if(primitives.size() == 0)
return end();

//start at the beginning of the primitive array
result.primitive_index = 0;

return result;
}

rtsOBJ::iterator rtsOBJ::end()
{
//create an end iterator to return
iterator result;
result.obj = this;
result.end_primitive = true;
result.primitive_index = result.obj->primitives.size();

return result;
}

void rtsOBJ::iterator::operator++()
{
primitive_index++;
if(primitive_index >= obj->primitives.size())
(*this) = obj->end();

}

bool rtsOBJ::iterator::operator ==(rtsOBJ::iterator operand)
{
if(operand.primitive_index == primitive_index)
return true;
else return false;
}

bool rtsOBJ::iterator::operator !=(rtsOBJ::iterator operand)
{
if(operand.primitive_index != primitive_index)
return true;
else return false;
}

unsigned int rtsOBJ::iterator::operator*()
{
return primitive_index;
}

void rtsOBJ::iterator::print()
{
cout<<"This is a test"<<endl;
}

#endif
``````