PDA

View Full Version : C++ opengl sfml obj loader help



Enlighten
04-19-2012, 01:22 PM
Hi Guys,

I'm building and object loader in the above language and libraries, I have a basic single file loaded in my loader and showing on the screen.

Now part of the assignment is to make the model of a robotic arm move, pivot etc.

In order to do this i guess i have to load every part of the arm model separately?

If so how would i do that?
My code is below

If you need any more info please ask

thanks

Main

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "model.h" //Model header


#define REFRESH_RATE 0.0167f //60th of a sec

int modelId; //The model id
model obj; //The model object


//Initialise OpenGL stuff, load the model and return the model's id
int init()
{

glClearColor(0.0,0.0,0.0,1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(150.f, 800.0/600.0, 1.f, 20.f);
glMatrixMode(GL_MODELVIEW);

glEnable(GL_DEPTH_TEST);

modelId=obj.loadModel("testPUMA.obj"); //load the torus.obj file

glEnable(GL_LIGHTING); //we enable lighting, to make the 3D object to 3D
glEnable(GL_LIGHT0);
float diffuse[]={1.0f,1.0f,1.0f,1.0f}; //light color is white
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);
float ambient[]={0.2f,0.2f,0.2f,1.0f}; //light color is white
glLightfv(GL_LIGHT0,GL_AMBIENT,ambient);

return modelId;
}

//Display the model given its id and an incremental angle of rotation
void display(int modelId, float angle)
{
glLoadIdentity();

glTranslatef(0.0,0.0,-5.0);
//glRotatef(angle,1.0,1.0,1.0);

glCallList(modelId); //draw the 3D mesh
}



////////////////////////////////////////////////////////////
/// Entry point of application
///
/// \return Application exit code
///
////////////////////////////////////////////////////////////
int main()
{
// Create the main window
//sf::Window App(sf::VideoMode(600, 600, 32), "SFML OpenGL");
//


sf::WindowSettings Settings;
Settings.DepthBits = 24; // Request a 24 bits depth buffer
Settings.StencilBits = 8; // Request a 8 bits stencil buffer
Settings.AntialiasingLevel = 2; // Request 2 levels of antialiasing
sf::Window App(sf::VideoMode(800, 600, 32), "OBJ Model Viewer", sf::Style::Close, Settings);

// Create a clock for measuring time elapsed
sf::Clock Clock;
// declare a struct to get the system time
SYSTEMTIME st;

float angle=0.0;

modelId=init();

// Start game loop
while (App.IsOpened())
{
// Process events
sf::Event Event;
while (App.GetEvent(Event))
{
// Close window : exit
if (Event.Type == sf::Event::Closed)
App.Close();

// Escape key : exit
if ((Event.Type == sf::Event::KeyPressed) &amp;&amp; (Event.Key.Code == sf::Key::Escape))
App.Close();

// Resize event : adjust viewport
if (Event.Type == sf::Event::Resized)
glViewport(0, 0, Event.Size.Width, Event.Size.Height);

if ((Event.Type == sf::Event::KeyPressed) ){
switch (Event.Key.Code){
case sf::Key::T: waist_rotate += MOVE_INCREMENT; break;
case sf::Key::G: waist_rotate -= MOVE_INCREMENT; break;
// other key bindings
}
}

}

// Set the active window before using OpenGL commands
// It's useless here because active window is always the same,
// but don't forget it if you use multiple windows or controls
App.SetActive();

float elapsedTime=Clock.GetElapsedTime();
if(elapsedTime>REFRESH_RATE){
// get current system time
GetSystemTime(&amp;st);

Clock.Reset();

angle+=0.5; //Incremental angle

if (angle>360)
angle-=360;
display(modelId,angle);
}

// Finally, display rendered frame on screen
App.Display();
}


return EXIT_SUCCESS;
}

model.cpp

/////////////////////////////////////////////////////////
//// /////
//// /////
//// Basic model class implementation /////
//// /////
//// Eric Tatham /////
//// /////
//// /////
/////////////////////////////////////////////////////////

#include "model.h"

//Constructor
model::model()
{
//Initialise id to 0
modelId=0;
}


//Load a model using given filename and return the model id
int model::loadModel(const char* filename)
{
ifstream in(filename); //Open the model file
if(!in.is_open()) //if not opened, exit with -1
{
cout << "File not opened" << std::endl;
return -1;
}

//Set up an array to act as a buffer for text read from the input file
char buf[256];

//Read input file text to end of file and push onto lines list
while(!in.eof())
{
in.getline(buf,256);
lines.push_back(new std::string(buf));
}

in.close();

//Now the file contents are in memory, parse the file's lines
ParseOBJFile();

//Check if the OBJ file has a linked material file
if (hasMaterialFile())
{
//Open the file
string name=GetMatFileName();

ifstream in(name); //Open the model file

if(!in.is_open()) //if not opened, exit with -1
{
std::cout << "File not opened" << std::endl;
return -1;
}

//Set up an array to act as a buffer for text read from the input file
char buf2[256];

//Read input file text to end of file and push onto matLines list
while(!in.eof())
{
in.getline(buf2,256);
matLines.push_back(new std::string(buf2));
}

in.close();

//Now we have a material file in memory, parse the lines of the file
ParseMTLFile();
}

//Now build the OpenGL List and get a model id
GLuint modelId=BuildOpenGL();

return modelId;
}



//Destructor
model::~model()
{
//delete everything to avoid memory leaks
for(int i=0;i<(int)lines.size();i++)
delete lines[i];
for(int i=0;i<(int)matLines.size();i++)
delete matLines[i];
lines.clear();
matLines.clear();
faces.clear();
}


//Parse the OBJ file once it has been opened and loaded into memory
void model::ParseOBJFile()
{
//There may be a material file name: mtllib
//and each face may have a usemtl proceeding it

char matFileName[32];
char usemtlName[32];
hasMaterialFile(false);
bool usemtl=false; //No valid usemtl name

size_t found_mtllib, found_usemtl;


//Go through each of the input text lines in the lines list, and decide what kind of element it is
for(int i=0;i<(int)lines.size();i++)
{

found_mtllib= lines[i]->find("mtllib");
found_usemtl= lines[i]->find("usemtl");


//Check for material file and, if found, get its file name
if (found_mtllib!=string::npos)
{
sscanf_s(lines[i]->c_str(),"mtllib %s",matFileName,32);
hasMaterialFile(true);
SetMatFileName( string(matFileName));
}

//Check for use material
else if (found_usemtl!=string::npos)
{
sscanf_s(lines[i]->c_str(),"usemtl %s",usemtlName,32);
usemtl=true;
}

//if first character is 'v' and second is ' ' then we have a vertex
//so get it and add it to the vertManager list
else if(lines[i]->c_str()[0]=='v' &amp;&amp; lines[i]->c_str()[1]==' ')
{
float tmpx,tmpy,tmpz;
//read in the 3 float coordinates to tmpx,tmpy,tmpz
sscanf_s(lines[i]->c_str(),"v %f %f %f",&amp;tmpx,&amp;tmpy,&amp;tmpz);
//and then make a point3D and push onto the model's vertices list
Point3D pt=Point3D(tmpx,tmpy,tmpz);
vertManager.AddVertex(pt);

//else if first character is 'v' and second is 'n' then we have a normal
//so get it and add it to the normManager list
}else if(lines[i]->c_str()[0]=='v' &amp;&amp; lines[i]->c_str()[1]=='n')
{
float tmpx,tmpy,tmpz;
//read in the 3 float coordinates to tmpx,tmpy,tmpz
sscanf_s(lines[i]->c_str(),"vn %f %f %f",&amp;tmpx,&amp;tmpy,&amp;tmpz);
//and then make a point3D to represent the normal vector and push onto the model's normals list
Normal norm=Normal(tmpx,tmpy,tmpz);
normManager.AddNormal(norm);

//else if first character is 'v' and second is 't' then we have a UV
//so get it and add it to the uvManager list
}else if(lines[i]->c_str()[0]=='v' &amp;&amp; lines[i]->c_str()[1]=='t')
{
float tmpx,tmpy;
//read in the 2 float coordinates to tmpx,tmpy
sscanf_s(lines[i]->c_str(),"vt %f %f",&amp;tmpx,&amp;tmpy);
//and then make a UV and push onto the model's texCoords list
UV uv=UV(tmpx,tmpy);
uvManager.AddUV(uv);

//else if first character is 'f' then we have a face
}else if(lines[i]->c_str()[0]=='f')
{
//Create temporary variable to hold the indices from the face input line
int v1,t1,n1,v2,t2,n2,v3,t3,n3,v4,t4,n4;

//Count the number of spaces in the input line
//If there are 3 spaces then we have a triangular face
//else we have a quad face
if(count(lines[i]->begin(),lines[i]->end(),' ')==3)
{
//Read the vertex, texture UV and normal indices from the input line
sscanf_s(lines[i]->c_str(),"f %d/%d/%d %d/%d/%d %d/%d/%d",&amp;v1,&amp;t1,&amp;n1,&amp;v2,&amp;t2,&amp;n2,&amp;v3,&amp;t3,&amp;n3); //Push the vertex and normal indices onto the faces list

int vert[4];
int norm[4];
int tex[4];

vert[0]=v1;
vert[1]=v2;
vert[2]=v3;
vert[3]=0;

norm[0]=n1;
norm[1]=n2;
norm[2]=n3;
norm[3]=0;

tex[0]=t1;
tex[1]=t2;
tex[2]=t3;
tex[3]=0;

Face face= Face(false,usemtl, string(usemtlName), vert, norm, tex);
faces.push_back(face);

}else //Must be a quad so there are 4 sets of data to read
{
//Read the vertex, texture UV and normal indices from the input line
sscanf_s(lines[i]->c_str(),"f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d",&amp;v1,&amp;t1,&amp;n1,&amp;v2,&amp;t2,&amp;n2,&amp;v3,&amp;t3,&amp;n3,&amp;v4,&amp;t4,&amp;n4 );
//Push the vertex and normal indices onto the faces list
int vert[4];
int norm[4];
int tex[4];

vert[0]=v1;
vert[1]=v2;
vert[2]=v3;
vert[3]=v4;

norm[0]=n1;
norm[1]=n2;
norm[2]=n3;
norm[3]=n4;

tex[0]=t1;
tex[1]=t2;
tex[2]=t3;
tex[3]=t4;

Face face= Face(true,usemtl, string(usemtlName), vert, norm, tex);
faces.push_back(face);

}
}
}
}




//Parse the MTL file
void model:: ParseMTLFile()
{
size_t found_newmtl, found_Kd, found_mapKd,found_Ka,found_Ks;

//Loop through each line of the file looking for relevant strings
for(int i=0;i<(int)matLines.size();i++)
{
//In each of the following, the found_*** variable is set to the position of the string if found
found_newmtl = matLines[i]->find("newmtl");
//look for map_Kd BEFORE looking for Kd
//to ensure that map_Kd line is not accepted as Kd because Kd characters are found within it
found_mapKd = matLines[i]->find("map_Kd");
found_Kd = matLines[i]->find("Kd");
found_Ks = matLines[i]->find("Ks");
found_Ka = matLines[i]->find("Ka");

/*

TO DO 1

Code is needed here to parse through the MTL file

You can check whether certain string is found as follows:

if (found_newmtl!=string::npos)

This, of course, checks for the newmtl string.

string::npos has a value representing the end of line position.
Thus the above check is true only if the string has been found.

If a newmtl line is found:
Create a new Material and add it to the materialManager

i.e.
Material material = Material();
material.SetName(string(newmtlName));
materialManager.AddMaterial(string(newmtlName),mat erial);

If Kd line is found:
Set the materials diffuse colour via the materialManager

i.e.
kd.SetRed(tmpr);kd.SetGreen(tmpg);kd.SetBlue(tmpb) ;
//Set it for the current named material through the material manager
materialManager.SetDiffuse(newmtlName,kd);

To start with, I recommend just dealing with diffuse material colour
You can always come back later to add Ambient, Specular, Shininess, etc..

If map_Kd line is found:
Get the texture file name and set up the texture via the materialManager

i.e.
materialManager.SetUpTexture(newmtlName, textureFilename);

Note that SetUpTexture uses TextureManager to load the texture and provide a reference ID for it.
It also sets the Material textureId to the OpenGl texture ID which the TextureManager provides.
It can be used later to reference this texture after recovery via the materialManager GetTextureId() method


*/

}


}



//Build the OpenGL for the model
GLuint model::BuildOpenGL()
{
GLuint modId=glGenLists(1);

//Create the OpenGL list
glNewList(modId,GL_COMPILE);

//Set up some temporary variables
//float ambient[4];
//float diffuse[4];
//float specular[4];
//float shininess;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


//for each face in the model
for(int i=0;i<(int)faces.size();i++)
{

/*


TO DO 2

Code is needed here to check whether the current face has a material defined

If so, then:

get its name and use the materialManager to check if it has a texture defined

If it does have a texture then get the texture ID through the materialManager, bind and enable the texture
else get the material data through the materialManager and set up glMaterialfv(****) calls.


*/

//if the face is a quad then there are 4 vertices and 4 normals to deal with
if(faces[i].IsQuad())
{
glBegin(GL_QUADS);


//Recover the vertex, normal and UV from the respective Managers
//For Vertex 1
int vertId = faces[i].GetVertexIndex1();
int normId = faces[i].GetNormalIndex1();
int texId = faces[i].GetUVIndex1();

Point3D vert = vertManager.GetVertex(vertId);
Normal norm = normManager.GetNormal(normId);
UV tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


//For Vertex 2
vertId = faces[i].GetVertexIndex2();
normId = faces[i].GetNormalIndex2();
texId = faces[i].GetUVIndex2();

vert = vertManager.GetVertex(vertId);
norm = normManager.GetNormal(normId);
tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


//For Vertex 3
vertId = faces[i].GetVertexIndex3();
normId = faces[i].GetNormalIndex3();
texId = faces[i].GetUVIndex3();

vert = vertManager.GetVertex(vertId);
norm = normManager.GetNormal(normId);
tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


//For Vertex 4
vertId = faces[i].GetVertexIndex4();
normId = faces[i].GetNormalIndex4();
texId = faces[i].GetUVIndex4();

vert = vertManager.GetVertex(vertId);
norm = normManager.GetNormal(normId);
tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


glEnd();
}
//else there must be 3 vertices and 3 normals to deal with
else
{
glBegin(GL_TRIANGLES);
//Recover the vertex, normal and UV from the respective Managers
//For Vertex 1
int vertId = faces[i].GetVertexIndex1();
int normId = faces[i].GetNormalIndex1();
int texId = faces[i].GetUVIndex1();

Point3D vert = vertManager.GetVertex(vertId);
Normal norm = normManager.GetNormal(normId);
UV tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


//For Vertex 2
vertId = faces[i].GetVertexIndex2();
normId = faces[i].GetNormalIndex2();
texId = faces[i].GetUVIndex2();

vert = vertManager.GetVertex(vertId);
norm = normManager.GetNormal(normId);
tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());


//For Vertex 3
vertId = faces[i].GetVertexIndex3();
normId = faces[i].GetNormalIndex3();
texId = faces[i].GetUVIndex3();

vert = vertManager.GetVertex(vertId);
norm = normManager.GetNormal(normId);
tex = uvManager.GetUV(texId);

glNormal3f(norm.GetX(),norm.GetY(),norm.GetZ());
glTexCoord2f(tex.GetU(),tex.GetV());
glVertex3f(vert.GetX(),vert.GetY(),vert.GetZ());

glEnd();
}
}
glEndList();

return modId;

}

tonyo_au
04-19-2012, 08:21 PM
you could either split the model into several separate objs or colour code different parts of the models and create a separate mesh per material in the obj

Enlighten
04-22-2012, 05:59 AM
Thanks,

Ok so if i split the model into several separate objs in maya and export them separately, how would i get the loader to load them individually?

Thankyou very much