I've created a shape in Blender (I've made sure to triangulate faces and add normals) which I've exported in .obj format to use in an openframeworks project. I've also wrote a small class to parse this .obj file. The shape is drawing perfectly, but I can't seem to get the normals to apply correctly.
The export only contains vertices, normals, and faces. As dictated by the .obj format: I add vertices to a mesh and use the supplied indices to draw faces. The normals, and indices for normals, are supplied in a similar fashion. Indicated by the image below, I'm not applying them correctly.
Here's my model loader script:
modelLoader.h
#ifndef _WAVEFRONTLOADER
#define _WAVEFRONTLOADER
#include "ofMain.h"
class waveFrontLoader {
public:
ofMesh mesh;
waveFrontLoader();
~waveFrontLoader();
void loadFile(char *fileName);
ofMesh generateMesh();
private:
typedef struct
{
ofIndexType v1,v2,v3;
ofIndexType vn1,vn2,vn3;
}
Index;
std::vector<ofVec3f> vertices;
std::vector<ofVec3f> normals;
std::vector<Index> indices;
void parseLine(char *line);
void parseVertex(char *line);
void parseNormal(char *line);
void parseFace(char *line);
};
#endif
modelLoader.cpp
#include "waveFrontLoader.h"
waveFrontLoader::waveFrontLoader()
{
}
void waveFrontLoader::loadFile(char *fileName)
{
ifstream file;
char line[255];
//open file in openframeworks data folder
file.open(ofToDataPath(fileName).c_str());
if (file.is_open())
{
while (file.getline(line,255))
{
parseLine(line);
}
}
}
void waveFrontLoader::parseLine(char *line)
{
//If empty, don't do anything with it
if(!strlen(line))
{
return;
}
//get line type identifier from char string
char *lineType = strtok(strdup(line), " ");
//parse line depending on type
if (!strcmp(lineType, "v")) // Vertices
{
parseVertex(line);
}
else if (!strcmp(lineType, "vn")) // Normals
{
parseNormal(line);
}
else if (!strcmp(lineType, "f")) // Indices (Faces)
{
parseFace(line);
}
}
void waveFrontLoader::parseVertex(char *line)
{
float x;
float y;
float z;
vertices.push_back(ofVec3f(x,y,z));
//get coordinates from vertex line and assign
sscanf(line, "v %f %f %f", &vertices.back().x, &vertices.back().y, &vertices.back().z);
}
void waveFrontLoader::parseNormal(char *line)
{
float x;
float y;
float z;
normals.push_back(ofVec3f(x,y,z));
//get coordinates from normal line and assign
sscanf(line, "vn %f %f %f", &normals.back().x, &normals.back().y, &normals.back().z);
}
void waveFrontLoader::parseFace(char *line)
{
indices.push_back(Index());
//get vertex and normal indices
sscanf(line, "f %d//%d %d//%d %d//%d",
&indices.back().v1,
&indices.back().vn1,
&indices.back().v2,
&indices.back().vn2,
&indices.back().v3,
&indices.back().vn3);
}
ofMesh waveFrontLoader::generateMesh()
{
//add vertices to mesh
for (std::vector<ofVec3f>::iterator i = vertices.begin(); i != vertices.end(); ++i)
{
mesh.addVertex(*i);
}
//add indices to mesh
for (std::vector<Index>::iterator i = indices.begin(); i != indices.end(); ++i)
{
// -1 to count from 0
mesh.addIndex((i->v1) - 1);
mesh.addIndex((i->v2) - 1);
mesh.addIndex((i->v3) - 1);
mesh.addNormal(normals[(i->vn1) - 1]);
mesh.addNormal(normals[(i->vn2) - 1]);
mesh.addNormal(normals[(i->vn3) - 1]);
}
return mesh;
}
waveFrontLoader::~waveFrontLoader()
{
}
I've tried adding normals like this as well (makes sense since it's one normal per face):
mesh.addNormal(normals[(i->vn1) - 1]);
I've also tried adding normals only once per two triangles being drawn, and tried adding indices and normals before the indices. Neither of those worked either.
testApp.h
#pragma once
#include "ofMain.h"
#include "waveFrontLoader.h"
class testApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void exit();
waveFrontLoader *objectLoader;
ofMesh mesh;
ofEasyCam camera;
ofLight light;
};
testApp.cpp
#include "testApp.h"
//--------------------------------------------------------------
void testApp::setup()
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
ofBackground(10, 10, 10);
camera.setDistance(10);
light.setPosition(10,30,-25);
objectLoader = new waveFrontLoader();
objectLoader->loadFile("test.obj");
mesh = objectLoader->generateMesh();
}
//--------------------------------------------------------------
void testApp::update()
{
}
//--------------------------------------------------------------
void testApp::draw()
{
camera.begin();
light.enable();
mesh.draw();
light.disable();
camera.end();
}
void testApp::exit()
{
delete objectLoader;
}