I have create a program in C++ that contains also OpenGL and I want to create also an OBJ loader in order to load an OBJ file I have! I have already created two functions which are:
void ReadFile(model *md)
{
// Open the file for reading OBJINFO.TXT
ifstream obj_file("tree.obj");
if (obj_file.fail())
exit(1);
// Get the number of vertices
obj_file >> md->vertices;
// Get the number of faces
obj_file >> md->faces;
// Get the vertex coordinates
for (int i = 0; i < md->vertices; i++)
{
obj_file >> md->obj_points[i].x;
obj_file >> md->obj_points[i].y;
obj_file >> md->obj_points[i].z;
}
// Get the face structure
for (int i = 0; i < md->faces; i++)
{
obj_file >> md->obj_faces[i].vn[0];
obj_file >> md->obj_faces[i].vn[1];
obj_file >> md->obj_faces[i].vn[2];
obj_file >> md->obj_faces[i].vn[3];
}
obj_file.close();
}
void DisplayModel(model md)
{
glPushMatrix();
glBegin(GL_TRIANGLES);
for (int i = 0; i < md.faces; i++)
{
glVertex3f(md.obj_points[md.obj_faces[i].vn[0]-1].x, md.obj_points[md.obj_faces[i].vn[0]-1].y, md.obj_points[md.obj_faces[i].vn[0]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[1]-1].x, md.obj_points[md.obj_faces[i].vn[1]-1].y, md.obj_points[md.obj_faces[i].vn[1]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[2]-1].x, md.obj_points[md.obj_faces[i].vn[2]-1].y, md.obj_points[md.obj_faces[i].vn[2]-1].z);
glVertex3f(md.obj_points[md.obj_faces[i].vn[2]-1].x, md.obj_points[md.obj_faces[i].vn[2]-1].y, md.obj_points[md.obj_faces[i].vn[3]-1].z);
}
glEnd();
glPopMatrix();
}
The main problem is that when I compile the project and run it, nothing appears in viewport. I increased also the dimensions of viewport in case it is small in order to appear the object, but the situation remains the same! So I reach the result that I have done something wrong in these functions! Could anyone help me?
Also I give some values of my OBJ file:
v 0.158000 0.975000 0.151491 1.000000
v 0.188743 0.025000 0.173826 1.000000
v 0.196000 0.025000 0.151491 1.000000
v 0.158000 0.025000 0.151491 1.000000
v 0.169743 0.025000 0.187631 1.000000
v 0.146257 0.025000 0.187631 1.000000
v 0.127257 0.025000 0.173826 1.000000
vn 0.950370 0.038015 0.308791
vn 0.950370 0.038015 0.308791
vn 0.950370 0.038015 0.308791
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 -0.000000
vn 0.587380 0.038015 0.808418
f 1//1 2//2 3//3
f 4//4 3//5 2//6
f 1//7 5//8 2//9
f 4//10 2//11 5//12
f 1//13 6//14 5//15
f 4//16 5//17 6//18
f 1//19 7//20 6//21
// Get the number of vertices
obj_file >> md->vertices;
// Get the number of faces
obj_file >> md->faces;
Mikopos, read the spec again. That's not how OBJs work. You have to parse out the vertexes/texture coordinates/normals/faces as you go along.
Use something like this:
#include <GL/glut.h>
// https://github.com/g-truc/glm
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/component_wise.hpp>
#include <vector>
#include <fstream>
#include <sstream>
struct Vertex
{
glm::vec3 position;
glm::vec2 texcoord;
glm::vec3 normal;
};
struct VertRef
{
VertRef( int v, int vt, int vn ) : v(v), vt(vt), vn(vn) { }
int v, vt, vn;
};
std::vector< Vertex > LoadOBJ( std::istream& in )
{
std::vector< Vertex > verts;
std::vector< glm::vec4 > positions( 1, glm::vec4( 0, 0, 0, 0 ) );
std::vector< glm::vec3 > texcoords( 1, glm::vec3( 0, 0, 0 ) );
std::vector< glm::vec3 > normals( 1, glm::vec3( 0, 0, 0 ) );
std::string lineStr;
while( std::getline( in, lineStr ) )
{
std::istringstream lineSS( lineStr );
std::string lineType;
lineSS >> lineType;
// vertex
if( lineType == "v" )
{
float x = 0, y = 0, z = 0, w = 1;
lineSS >> x >> y >> z >> w;
positions.push_back( glm::vec4( x, y, z, w ) );
}
// texture
if( lineType == "vt" )
{
float u = 0, v = 0, w = 0;
lineSS >> u >> v >> w;
texcoords.push_back( glm::vec3( u, v, w ) );
}
// normal
if( lineType == "vn" )
{
float i = 0, j = 0, k = 0;
lineSS >> i >> j >> k;
normals.push_back( glm::normalize( glm::vec3( i, j, k ) ) );
}
// polygon
if( lineType == "f" )
{
std::vector< VertRef > refs;
std::string refStr;
while( lineSS >> refStr )
{
std::istringstream ref( refStr );
std::string vStr, vtStr, vnStr;
std::getline( ref, vStr, '/' );
std::getline( ref, vtStr, '/' );
std::getline( ref, vnStr, '/' );
int v = atoi( vStr.c_str() );
int vt = atoi( vtStr.c_str() );
int vn = atoi( vnStr.c_str() );
v = ( v >= 0 ? v : positions.size() + v );
vt = ( vt >= 0 ? vt : texcoords.size() + vt );
vn = ( vn >= 0 ? vn : normals.size() + vn );
refs.push_back( VertRef( v, vt, vn ) );
}
// triangulate, assuming n>3-gons are convex and coplanar
for( size_t i = 1; i+1 < refs.size(); ++i )
{
const VertRef* p[3] = { &refs[0], &refs[i], &refs[i+1] };
// http://www.opengl.org/wiki/Calculating_a_Surface_Normal
glm::vec3 U( positions[ p[1]->v ] - positions[ p[0]->v ] );
glm::vec3 V( positions[ p[2]->v ] - positions[ p[0]->v ] );
glm::vec3 faceNormal = glm::normalize( glm::cross( U, V ) );
for( size_t j = 0; j < 3; ++j )
{
Vertex vert;
vert.position = glm::vec3( positions[ p[j]->v ] );
vert.texcoord = glm::vec2( texcoords[ p[j]->vt ] );
vert.normal = ( p[j]->vn != 0 ? normals[ p[j]->vn ] : faceNormal );
verts.push_back( vert );
}
}
}
}
return verts;
}
int btn;
glm::ivec2 startMouse;
glm::ivec2 startRot, curRot;
glm::ivec2 startTrans, curTrans;
void mouse(int button, int state, int x, int y )
{
if( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = glm::ivec2( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
startRot = curRot;
}
if( button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN )
{
btn = button;
startMouse = glm::ivec2( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
startTrans = curTrans;
}
}
void motion( int x, int y )
{
glm::ivec2 curMouse( x, glutGet( GLUT_WINDOW_HEIGHT ) - y );
if( btn == GLUT_LEFT_BUTTON )
{
curRot = startRot + ( curMouse - startMouse );
}
else if( btn == GLUT_RIGHT_BUTTON )
{
curTrans = startTrans + ( curMouse - startMouse );
}
glutPostRedisplay();
}
std::vector< Vertex > model;
void display()
{
glClearColor( 0.2f, 0.2f, 0.2f, 1.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glTranslatef( curTrans.x / w * 2, curTrans.y / h * 2, 0 );
gluPerspective( 60, ar, 0.1, 100 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0, 0, -10 );
glPushMatrix();
{
glRotatef( curRot.x % 360, 0, 1, 0 );
glRotatef( -curRot.y % 360, 1, 0, 0 );
// object
glColor3ub( 255, 0, 0 );
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, sizeof(Vertex), &model[0].position );
glTexCoordPointer( 2, GL_FLOAT, sizeof(Vertex), &model[0].texcoord );
glNormalPointer( GL_FLOAT, sizeof(Vertex), &model[0].normal );
glDrawArrays( GL_TRIANGLES, 0, model.size() );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
// bounding cube
glDisable( GL_LIGHTING );
glColor3ub( 255, 255, 255 );
glutWireCube( 7 );
glEnable( GL_LIGHTING );
}
glPopMatrix();
glutSwapBuffers();
}
// return the min/max points of pts
template< typename Vec >
std::pair< Vec, Vec > GetExtents( const Vec* pts, size_t stride, size_t count )
{
unsigned char* base = (unsigned char*)pts;
Vec pmin( *(Vec*)base );
Vec pmax( *(Vec*)base );
for( size_t i = 0; i < count; ++i, base += stride )
{
const Vec& pt = *(Vec*)base;
pmin = glm::min( pmin, pt );
pmax = glm::max( pmax, pt );
}
return std::make_pair( pmin, pmax );
}
// centers geometry around the origin
// and scales it to fit in a size^3 box
template< typename Vec >
void CenterAndScale( Vec* pts, size_t stride, size_t count, const typename Vec::value_type& size )
{
typedef typename Vec::value_type Scalar;
// get min/max extents
std::pair< Vec, Vec > exts = GetExtents( pts, stride, count );
// center and scale
const Vec center = ( exts.first * Scalar( 0.5 ) ) + ( exts.second * Scalar( 0.5f ) );
const Scalar factor = size / glm::compMax( exts.second - exts.first );
unsigned char* base = (unsigned char*)pts;
for( size_t i = 0; i < count; ++i, base += stride )
{
Vec& pt = *(Vec*)base;
pt = ( ( pt - center ) * factor );
}
}
int main( int argc, char **argv )
{
// https://en.wikipedia.org/wiki/Stanford_bunny
std::ifstream ifile( "bunny.obj" );
model = LoadOBJ( ifile );
CenterAndScale( &model[0].position, sizeof( Vertex ), model.size(), 7 );
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "OBJ" );
glutDisplayFunc( display );
glutMouseFunc( mouse );
glutMotionFunc( motion );
glEnable( GL_DEPTH_TEST );
// set up "headlamp"-like light
glShadeModel( GL_SMOOTH );
glEnable( GL_COLOR_MATERIAL );
glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
GLfloat position[] = { 0, 0, 1, 0 };
glLightfv( GL_LIGHT0, GL_POSITION, position );
glPolygonMode( GL_FRONT, GL_FILL );
glPolygonMode( GL_BACK, GL_LINE );
glutMainLoop();
return 0;
}
LMB-drag rotates, RMB-drag "pans".
And use this:
v 0.000000 2.000000 2.000000
v 0.000000 0.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 2.000000 2.000000
f -4 -3 -2 -1
v 2.000000 2.000000 0.000000
v 2.000000 0.000000 0.000000
v 0.000000 0.000000 0.000000
v 0.000000 2.000000 0.000000
f -4 -3 -2 -1
v 2.000000 2.000000 2.000000
v 2.000000 0.000000 2.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
f -4 -3 -2 -1
v 0.000000 2.000000 0.000000
v 0.000000 2.000000 2.000000
v 2.000000 2.000000 2.000000
v 2.000000 2.000000 0.000000
f -4 -3 -2 -1
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 0.000000 0.000000 2.000000
v 0.000000 2.000000 2.000000
f -4 -3 -2 -1
v 0.000000 0.000000 2.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 0.000000 2.000000
f -4 -3 -2 -1
Or this:
v 0.000000 2.000000 0.000000
v 0.000000 0.000000 0.000000
v 2.000000 0.000000 0.000000
v 2.000000 2.000000 0.000000
v 4.000000 0.000000 -1.255298
v 4.000000 2.000000 -1.255298
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.276597 0.000000 0.960986
vn 0.276597 0.000000 0.960986
vn 0.531611 0.000000 0.846988
vn 0.531611 0.000000 0.846988
# 6 vertices
# 6 normals
g all
s 1
f 1//1 2//2 3//3 4//4
f 4//4 3//3 5//5 6//6
# 2 elements
for test.obj.
Related
I've been working on a Vulkan based graphical engine, and whilst trying to create a buffer,
I have encountered an issue with the vkMapMemory() function. In the code shown below,
the program returns an "Access is denied" (-1073741819 or C0000005) error. Error happends inside the vkMapMemory() call, and I can't check its returned VkResult.
void createVertexBuffer(size_t sizeOfBuffer, void * _vertices , VkBuffer * _vertexBuffer, VkDeviceMemory * _vertexBufferMemory)
{
VkDeviceSize bufferSize = sizeOfBuffer;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer, &stagingBufferMemory);
void* data;
if(vkMapMemory(_device, *_vertexBufferMemory, 0, bufferSize, 0, &data) != VK_SUCCESS)
printf("This is not printed");
memcpy(data, _vertices, sizeOfBuffer);
vkUnmapMemory(_device, *_vertexBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, _vertexBuffer, _vertexBufferMemory);
copyBuffer(stagingBuffer, *_vertexBuffer, bufferSize);
vkDestroyBuffer(_device, stagingBuffer, NULL);
vkFreeMemory(_device, stagingBufferMemory, NULL);
return;
}
For some additional information:
sizeOfBuffer in this case is equal to 96
The _vertices array size is indeed also equal to 96, given that it looks like this:
_vertices[0][0] -1.000000 0.000000
_vertices[0][1] -1.000000 0.000000
_vertices[0][2] 0.000000 0.000000
_vertices[1][0] 1.000000 0.000000
_vertices[1][1] -1.000000 0.000000
_vertices[1][2] 0.000000 0.000000
_vertices[2][0] 1.000000 0.000000
_vertices[2][1] 1.000000 0.000000
_vertices[2][2] 0.000000 0.000000
_vertices[3][0] -1.000000 0.000000
_vertices[3][1] 1.000000 0.000000
_vertices[3][2] 0.000000 0.000000
The size of three lines is equal to 24 because (sizeof(glm::vec3)=12) and three lines contain two glm::vec3 structs:
typedef struct MKTAGA2 {
glm::vec3 position;
glm::vec3 color;
} MKTAGA;
I was mapping the _vertexBufferMemory when I should've been mapping the stagingBufferMemory
here is the fixed code:
void createVertexBuffer(size_t sizeOfBuffer, void * _vertices , VkBuffer * _vertexBuffer, VkDeviceMemory * _vertexBufferMemory)
{
VkDeviceSize bufferSize = sizeOfBuffer;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &stagingBuffer, &stagingBufferMemory);
void* data;
vkMapMemory(_device, stagingBufferMemory, 0, bufferSize, 0, &data);
memcpy(data, _vertices, sizeOfBuffer);
vkUnmapMemory(_device, stagingBufferMemory);
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, _vertexBuffer, _vertexBufferMemory);
copyBuffer(stagingBuffer, *_vertexBuffer, bufferSize);
vkDestroyBuffer(_device, stagingBuffer, NULL);
vkFreeMemory(_device, stagingBufferMemory, NULL);
return;
}
I am using glDrawElement() for rendering a plain but the indexing is not working as intended so i am getting an irrelevant object .I have used AAssetmanager for loading the coordinates from a wavefront object file .
here is my code for rendering :
GLushort squareindices[] = {
0 , 1 ,2,
0, 3, 2
};
GLfloat vertexColor[] = {
1.0f, 1.0f , 0.0f,
0.0f , 0.0f , 1.0f,
1.0f, 1.0f , 0.0f,
0.0f , 0.0f , 1.0f,
};
void flatPlain::render(GLuint program) {
if (renderSelf) {
// flatPlainProgram = program;
auto *gens = new programGenerator;
auto *mats = new matrixOperation;
flatPlainProgram = gens->createProgram(vertexplain, fragmentplain);
// LOGE2("%x" , flatPlainProgram);
vertexLocation = glGetAttribLocation(flatPlainProgram, "vertexPosition");
// textureLocation = glGetAttribLocation(flatPlainProgram, "texturecord");
vertexColorlocation = glGetAttribLocation(flatPlainProgram, "vertexColour");
projection = glGetUniformLocation(flatPlainProgram, "projection");
model = glGetUniformLocation(flatPlainProgram, "modelView");
// sampleLocation = glGetUniformLocation(flatPlainProgram, "texture");
mats->perspective(projectionMatrix, (float) fov, (float) w / (float) h, 0.1f, 100.0f);
// if ( image != nullptr)
// loadTexture();
mats->createVolume(modelMatrix);
mats->rotateX(modelMatrix, angle);
mats->translateMatrix(x, y, z, modelMatrix);
glUseProgram(flatPlainProgram);
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, vertexCord);
glEnableVertexAttribArray(vertexLocation);
glVertexAttribPointer(vertexColorlocation, 3, GL_FLOAT, GL_FALSE, 0, vertexColor);
glEnableVertexAttribArray(vertexColorlocation);
glUniformMatrix4fv(projection, 1, GL_FALSE, projectionMatrix);
glUniformMatrix4fv(model, 1, GL_FALSE, modelMatrix);
// glUniform1i(sampleLocation, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, squareindices);
glDeleteProgram(flatPlainProgram);
}
}
I have read the vertices and then rendered it.
my .obj file:
v 0.000000 0.000000 0.000000
v 10.000000 0.000000 0.000000
v 0.000000 0.000000 -10.000000
v 10.000000 0.000000 -10.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.0000 1.0000 0.0000
usemtl None
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
my output:
This are your vertex coordinates
0: ( 0.0, 0.0, 0.0)
1: (10.0, 0.0, 0.0)
2: ( 0.0, 0.0, -10.0)
3: (10.0, 0.0, -10.0)
0 1
+-----+
| |
| |
+-----+
2 3
Triangulate the quad with the following indices:
0, 1, 2, 1, 3, 2
0 1
+-----+ +
| / / |
| / / |
+ +-----+
2 3
I created a .obj loader by modifying some code i found online, to parse to .obj file I do the following
int mesh::getMesh(char* inputFile, std::vector<std::vector<glm::vec3>> & out_vertices,std::vector < glm::vec2 > & out_uvs,std::vector < glm::vec3 > & out_normals){
std::cout << "Attempting to load file as model " << inputFile << "\n";
std::vector<std::vector<unsigned int>> vertexIndices, uvIndices, normalIndices;
std::vector<glm::vec3> temp_vertices;
std::vector<glm::vec2> temp_uvs;
std::vector<glm::vec3> temp_normals;
FILE * file = fopen(inputFile, "r");
if( file == NULL ){
std::cout << "Impossible to open the file !\n";
return 0;
}
while( 1 ){
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // EOF = End Of File. Quit the loop.
if ( strcmp( lineHeader, "v" ) == 0 ){
glm::vec3 vertex;
fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z );
temp_vertices.push_back(vertex);
}else if ( strcmp( lineHeader, "vt" ) == 0 ){
glm::vec2 uv;
fscanf(file, "%f %f\n", &uv.x, &uv.y );
temp_uvs.push_back(uv);
}else if ( strcmp( lineHeader, "vn" ) == 0 ){
glm::vec3 normal;
fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
temp_normals.push_back(normal);
}else if ( strcmp( lineHeader, "f" ) == 0 ){
unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] );
if (matches != 9){
std::cout << "File can't be read by the parser : ( Try exporting with other options )\n";
return 0;
}
std::vector<unsigned int> vertexTriangle;
vertexTriangle.push_back(vertexIndex[0]);
vertexTriangle.push_back(vertexIndex[1]);
vertexTriangle.push_back(vertexIndex[2]);
vertexIndices.push_back(vertexTriangle);
//uvIndices .push_back(uvIndex[0]);
//uvIndices .push_back(uvIndex[1]);
//uvIndices .push_back(uvIndex[2]);
//std::vector<unsigned int> normalTriangle;
//normalTriangle.push_back(normalIndex[0]);
//normalTriangle.push_back(normalIndex[1]);
//normalTriangle.push_back(normalIndex[2]);
//normalIndices.push_back(normalTriangle);
}
}
for( unsigned int i=0; i<vertexIndices.size(); i++ ){
std::vector<unsigned int> vertexIndexs=vertexIndices[i];
//glm::vec3 vertex0 = temp_vertices[ vertexIndexs[3]-1 ];
glm::vec3 vertex1 = temp_vertices[ vertexIndexs[0]-1 ];
glm::vec3 vertex2 = temp_vertices[ vertexIndexs[1]-1 ];
glm::vec3 vertex3 = temp_vertices[ vertexIndexs[2]-1 ];
std::vector<glm::vec3> vertex;
vertex.push_back(vertex1); vertex.push_back(vertex2); vertex.push_back(vertex3);
out_vertices.push_back(vertex);
}
return 1;
}
I then draw it using
<initialization...>
std::vector<std::vector<glm::vec3>> vertices;
std::vector<glm::vec2> uvs;
std::vector<glm::vec3> normals;
mesh::getMesh("Resources/Models/cube.obj",vertices,uvs,normals);
<window handling and whatnot...>
for (unsigned i=0; i < vertices.size(); i++) {
std::vector<glm::vec3> vertexGroup = vertices[i];
glBegin(GL_TRIANGLES);
glVertex3f(vertexGroup[0].x,vertexGroup[0].y,vertexGroup[0].z);
glVertex3f(vertexGroup[1].x,vertexGroup[1].y,vertexGroup[1].z);
glVertex3f(vertexGroup[2].x,vertexGroup[2].y,vertexGroup[2].z);
glEnd();
}
This works fine for this .obj file
# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8
but when i move onto something more complicated, like A low poly model of a man i took from a free model site as a testbed, its missing a triangle to every face (see screenshots below)
The Cube:
The Humanoid
The man renders fine in blender so it must be how I'm parsing the file?
You need to triangulate the faces in blender while exporting the model to your format. For obj models you always export with normals attached, and the vertices triangulated. You may also need to check "Keep vertex order".
Ok, so I have this FBO with a depth buffer and 4 color attachments. I want to read the dept buffer at the place where I hold the cursor and a value from the fourth color attachment. I don't seem to manage any of the two.
I've wrote this class :
class Picker3D{
public:
bool useAtachment;
GLenum atachment;
float PickerLastSignature;
point3f PickerLastPos;
Picker3D( bool useAtachment = true , GLenum atachment = GL_COLOR_ATTACHMENT3 ) : useAtachment(useAtachment) , atachment(atachment) {
}
void SetObjectSignature( float signature , GLSL_Prog prog ){
glUniform1f( glGetUniformLocation( prog.PR , "Signature" ) , signature );
}
float getSignature( float x , float y , float height ){
glGetError();
if( useAtachment )
glReadBuffer( atachment );
cout << " Error message1 : " << glGetError() << endl ;
float *pixels = new float[4];
glReadPixels( x , height - y , 1 , 1 , GL_RGB , GL_FLOAT , pixels );
PickerLastSignature = pixels[1];
cout << " Error message2 : " << glGetError() << endl ;
return PickerLastSignature;
}
point3f get3DPosition( float x , float y , float height ){
//glReadBuffer( GL_DEPTH_ATTACHMENT );
double depth;
glReadPixels( x , y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , &depth );
cout << depth << endl;
int viewport[4];
float *modelview;
double projection[16];
double ModelView[16];
modelview = mat4f::GetTopMatrix().returnTransposedMatrix().returnFloatArray();
for( int i = 0 ; i < 16 ; i++ ){
ModelView[i] = modelview[i];
}
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
double x1 , y1 , z1;
gluUnProject( x , height - y , depth , ModelView , projection , viewport , &x1 , &y1 , &z1 );
PickerLastPos = point3f( x1 , y1 , z1 );
return PickerLastPos;
}
};
and I use it like so
// in main loop
BindFrameBuffer();
drawStuff();
Picker.get3DPosition( x , y , height );
Picker.getSignature( x , y , height );
UnbindBuffer();
drawSecondPass();
endDrawSecondPass();
// check Values of PickerLastPos and PickerLastSignature
SwapBuffers();
the depth is not changing altrough it is working on the screen and the getSignature gives error 1282( invalid operation, I think ) at the line with glReadBuffer.
the main loop is :
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
/// deferred pass
DefFBO->drawFBO();
GeometryShader.UseNow();
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
// load the view matrix and set the camera
cam.camera_set();
cam.camera_get( GeometryShader );
// load identity for model matrix
mat4f::LoadIdentity();
/// Geometry Drawing
mat4f::PushMatrix();
mat4f::Translate( 0 , 0 , -10 );
Picker.SetObjectSignature( 1.0f , GeometryShader );
T90.obj_rend( GeometryShader );
Picker.SetObjectSignature( 2.0f , GeometryShader );
Town.obj_rend( GeometryShader );
mat4f::PopMatrix();
Picker.get3DPosition( Window.Mouse->mouse_x , Window.Mouse->mouse_y , Window.Height );
/// setting the deferred read from buffer part
// last read from framebuffer
Picker.getSignature( Window.Mouse->mouse_x , Window.Mouse->mouse_y , Window.Height );
// unbind FBO
DefFBO->readFBO();
// use fragment shader, for lights and for post processing
FragmentShader.UseNow();
// send windows width and height and bind multiple textures
DefFBO->send_to_GLSL( FragmentShader.PR );
// send view matrix to the shader
cam.camera_get( FragmentShader );
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glUniform3f( glGetUniformLocation( FragmentShader.PR , "CameraPosition" ) , cam.x , cam.y , cam.z );
/// final draw
DefFBO->sendGlobalLight( FragmentShader , point3f( 0 , 1 , 0 ) , point3f( 0.3 , 0.3 , 0.3 ) , point3f( 0.9 , 0.9 , 0.9 ) );
cout << Picker.PickerLastPos << endl;
DefFBO->end_read();
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
It seems that I had to bind the fbo for reading for geting data out of it, as #Andon pointed out. I was binding with GL_DRAW_FRAMEBUFFER.
I am using an object loader to load an obj model that I exported from blender. I can load the model but none of the textures are loaded and I don't know why. Should they be loaded when I render the model in the main class or do I need to load the textures as well as rendering the model?
objloader.cpp
/************************************************************
*Loads obj file - limited to vertices, faces, normals, texture maps
*loads to object structure as defined in .h file
************************************************************/
#include <stdio.h>
#include <iostream>
#include <io.h>
#include <stdlib.h>
#include "glut.h"
#include "objloader.h"
using namespace std;
void object_type::render()
{
glBegin(GL_TRIANGLES); // glBegin and glEnd delimit the vertices that define a primitive (in our case triangles)
for (int j=0;j<polygons_qty;j++)
{
//----------------- FIRST VERTEX -----------------
//Normal coordinates of the first vertex
glNormal3f( normcoord[ polygon[j].n[0] - 1 ].i,
normcoord[ polygon[j].n[0] - 1 ].j,
normcoord[ polygon[j].n[0] - 1 ].k);
// Texture coordinates of the first vertex
glTexCoord2f( mapcoord[ polygon[j].t[0] - 1 ].u,
mapcoord[ polygon[j].t[0] - 1 ].v);
// Coordinates of the first vertex
glVertex3f( vertex[ polygon[j].v[0] - 1].x,
vertex[ polygon[j].v[0] - 1].y,
vertex[ polygon[j].v[0] - 1].z);
//----------------- SECOND VERTEX -----------------
//Normal coordinates of the first vertex
glNormal3f( normcoord[ polygon[j].n[1] - 1 ].i,
normcoord[ polygon[j].n[1] - 1 ].j,
normcoord[ polygon[j].n[1] - 1 ].k);
// Texture coordinates of the first vertex
glTexCoord2f( mapcoord[ polygon[j].t[1] - 1 ].u,
mapcoord[ polygon[j].t[1] - 1 ].v);
// Coordinates of the first vertex
glVertex3f( vertex[ polygon[j].v[1] - 1].x,
vertex[ polygon[j].v[1] - 1].y,
vertex[ polygon[j].v[1] - 1].z);
//----------------- THIRD VERTEX -----------------
//Normal coordinates of the first vertex
glNormal3f( normcoord[ polygon[j].n[2] - 1 ].i,
normcoord[ polygon[j].n[2] - 1 ].j,
normcoord[ polygon[j].n[2] - 1 ].k);
// Texture coordinates of the first vertex
glTexCoord2f( mapcoord[ polygon[j].t[2] - 1 ].u,
mapcoord[ polygon[j].t[2] - 1 ].v);
// Coordinates of the first vertex
glVertex3f( vertex[ polygon[j].v[2] - 1].x,
vertex[ polygon[j].v[2] - 1].y,
vertex[ polygon[j].v[2] - 1].z);
}
glEnd();
}
/*
vertex_type vertex[MAX_VERTICES];
mapcoord_type mapcoord[MAX_VERTICES];
normcoord_type normcoord[MAX_NORMALS];
polygon_type polygon[MAX_POLYGONS];
int id_texture
*/
int object_type::objdatadisplay()
{
int i;
printf("VERTICES: %d\n",vertices_qty);
for (i =0;i<vertices_qty;i++)
{
printf("%f %f %f\n",vertex[i].x,vertex[i].y,vertex[i].z);
}
printf("NORMALS: %d\n",normcoord_qty);
for (i =0;i<normcoord_qty;i++)
{
printf("%f %f %f\n",normcoord[i].i,normcoord[i].j,normcoord[i].k);
}
printf("MAP COORDS: %d\n",mapcoord_qty);
for (i =0;i<mapcoord_qty;i++)
{
printf("%f %f\n",mapcoord[i].u,mapcoord[i].v);
}
printf("POLYGONS: %d\n",polygons_qty);
for (i=0;i<polygons_qty;i++) //for each vertex of polygon (triangle)
{
for (int j = 0;j<3;j++)
{
printf("%d::%d/%d/%d\n",i,polygon[i].v[j],polygon[i].t[j],polygon[i].n[j]);
}
}
return 1;
}
int object_type::objloader(char *p_filename)
{
int ivertex=0; //Index variable
int inormal =0;
int ipolygon=0;
int imap=0;
char string[256];
FILE *l_file; //File pointer
char l_char; //Char variable
unsigned short l_face_flags; //Flag that stores some face information
if ((l_file=fopen (p_filename, "rt"))== NULL) return 0; //Open the file
while (!feof(l_file)) //Loop to scan the whole file
{
fscanf(l_file,"%c",&l_char);
if(l_char=='\n')//read char if'/n' -skip to next and read
fscanf(l_file,"%c",&l_char);
switch (l_char) //parse
{
default: fgets(string,256,l_file);
break;
case 'v': //a vertex or a normal or a text co-ord
fscanf(l_file,"%c",&l_char);
switch (l_char)
{
case ' ': //a vertex -expect and so read 3 floats next
fscanf(l_file,"%f %f %f",&vertex[ivertex].x, &vertex[ivertex].y,&vertex[ivertex].z);
ivertex++;
break;
case 'n': //a normal -expect and so read 3 floats next
fscanf(l_file,"%f %f %f",&normcoord[inormal].i, &normcoord[inormal].j,&normcoord[inormal].k);
inormal++;
break;
case 't': //a texture map coord-expect and so read 2 floats next
fscanf(l_file,"%f %f",&mapcoord[imap].u, &mapcoord[imap].v);
imap++;
break;
} //end switch
break;
case 'f': //a face read next assume format is -> f 1/1/1 2/2/2 3/3/3
for (int i=0;i<3;i++) //for each vertex of polygon (triangle)
{
fscanf(l_file,"%c",&l_char); //read space char - ignore this
fscanf(l_file,"%d",&polygon[ipolygon].v[i]); //read vertex.
fscanf(l_file,"%c",&l_char); //read space char - ignore this
fscanf(l_file,"%d",&polygon[ipolygon].t[i]); //read text coord.
fscanf(l_file,"%c",&l_char); //read space char - ignore this
fscanf(l_file,"%d",&polygon[ipolygon].n[i]); //read normal.
}
ipolygon++;
break;
} //end switch
}
fclose (l_file); // Closes the file stream
vertices_qty = ivertex;
polygons_qty = ipolygon;
mapcoord_qty = imap;
normcoord_qty = inormal;
return 1; //if successful
}
objloader.h
#ifndef OBJLOAD
#define OBJLOAD
/************************************************
*Loads obj file - limited to vertices, faces, normals, texture maps
*loads to object structure as defined in .h file
****************************************************/
#define MAX_VERTICES 8000 // Max number of vertices (for each object)
#define MAX_POLYGONS 8000 // Max number of polygons (for each object)
#define MAX_NORMALS 8000 // Max number of polygons (for each object)
// Our vertex type
typedef struct{
float x,y,z;
}vertex_type;
// Our normal type
typedef struct{
float i,j,k;
}normcoord_type;
// The polygon (triangle), 3 numbers that aim 3 vertices
typedef struct{
int v[3],t[3],n[3];
}polygon_type;
// The mapcoord type, 2 texture coordinates for each vertex
typedef struct{
float u,v;
}mapcoord_type;
// The object type
class object_type{
public:
int id_texture;
object_type(){}
~object_type(){}
int objloader(char *p_filename);
int objdatadisplay();
void render();
private:
char name[20];
int vertices_qty;
int polygons_qty;
int mapcoord_qty;
int normcoord_qty;
vertex_type vertex[MAX_VERTICES];
mapcoord_type mapcoord[MAX_VERTICES];
normcoord_type normcoord[MAX_NORMALS];
polygon_type polygon[MAX_POLYGONS];
};
#endif
main.cpp
#include "objloader.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glew.h>
#ifdef __WIN32__
#include <windows.h>
#endif
#include "glut.h" //glut has all ogl relevant .h files included
int screen_width=800;
int screen_height=600;
//angle of rotation
float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, angle=0.0;
float cRadius = 10.0f; // our radius distance from our character
float lastx, lasty;
object_type *objarray[2]; //objects container for our world. Used throughout so global
//Lights settings
GLfloat light_ambient[]= { 0.1f, 0.1f, 0.1f, 0.1f };
GLfloat light_diffuse[]= { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat light_specular[]= { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat light_position[]= { 100.0f, 0.0f, -10.0f, 1.0f };
//Materials settings
GLfloat mat_ambient[]= { 0.5f, 0.5f, 0.0f, 0.0f };
GLfloat mat_diffuse[]= { 0.5f, 0.5f, 0.0f, 0.0f };
GLfloat mat_specular[]= { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat mat_shininess[]= { 1.0f };
/************************************
*
* SUBROUTINE init(void)
*
* Used to initialize OpenGL and to setup our world
*
************************************/
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0); // Clear background color to black
// Viewport transformation
glViewport(0,0,screen_width,screen_height);
// Projection transformation
glMatrixMode(GL_PROJECTION); // Specifies which matrix stack is the target for matrix operations
glLoadIdentity(); // We initialize the projection matrix as identity
gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,5.0f,10000.0f);
//Lights initialization and activation
glLightfv (GL_LIGHT1, GL_AMBIENT, light_ambient);
glLightfv (GL_LIGHT1, GL_DIFFUSE, light_diffuse);
glLightfv (GL_LIGHT1, GL_DIFFUSE, light_specular);
glLightfv (GL_LIGHT1, GL_POSITION, light_position);
glEnable (GL_LIGHT1);
glEnable (GL_LIGHTING);
//Materials initialization and activation
glMaterialfv (GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv (GL_FRONT, GL_DIFFUSE, mat_specular);
glMaterialfv (GL_FRONT, GL_POSITION, mat_shininess);
//Other initializations
glShadeModel(GL_SMOOTH); // Type of shading for the polygons
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Texture mapping perspective correction
glEnable(GL_TEXTURE_2D); // Texture mapping ON
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Polygon rasterization mode (polygon filled)
glEnable(GL_CULL_FACE); // Enable the back face culling
glEnable(GL_DEPTH_TEST); // Enable the depth test
glTranslatef(0.0f, 0.0f, -cRadius);
glRotatef(xrot,1.0,0.0,0.0);
angle++; //increase the angle
for (int i=0;i<2;i++)
{
printf("*************\n");
objarray[i] = new (object_type);
objarray[i]->objloader("C:/3dModels/Museum.obj");
objarray[i]->objdatadisplay();
}
}
/**********************************************************
*
* SUBROUTINE resize(int p_width, int p_height)
*
* This routine must be called everytime we resize our window.
*
* Input parameters: p_width = width in pixels of our viewport
* p_height = height in pixels of our viewport
*
*********************************************************/
void resize (int p_width, int p_height)
{
if (screen_width==0 && screen_height==0) exit(0);
screen_width=p_width; // We obtain the new screen width values and store it
screen_height=p_height; // Height value
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // We clear both the color and the depth buffer so to draw the next frame
glViewport(0,0,screen_width,screen_height); // Viewport transformation
glMatrixMode(GL_PROJECTION); // Projection transformation
glLoadIdentity(); // We initialize the projection matrix as identity
gluPerspective(45.0f,(GLfloat)screen_width/(GLfloat)screen_height,5.0f,10000.0f);
glutPostRedisplay (); // This command redraw the scene (it calls the same routine of glutDisplayFunc)
}
/**********************************************************
*
* SUBROUTINE keyboard(void)
*
* Subroutine to handle keyboard input
*
*********************************************************/
void keyboard (unsigned char key, int x, int y) {
if (key=='q')
{
xrot += 1;
if (xrot >360) xrot -= 360;
}
if (key=='z')
{
xrot -= 1;
if (xrot < -360) xrot += 360;
}
if (key=='w')
{
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos += float(sin(yrotrad));
zpos -= float(cos(yrotrad));
ypos -= float(sin(xrotrad));
}
if (key=='s')
{
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos -= float(sin(yrotrad));
zpos += float(cos(yrotrad));
ypos += float(sin(xrotrad));
}
if (key=='d')
{
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos += float(cos(yrotrad)) * 0.2;
zpos += float(sin(yrotrad)) * 0.2;
}
if (key=='a')
{
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos -= float(cos(yrotrad)) * 0.2;
zpos -= float(sin(yrotrad)) * 0.2;
}
if (key==27)
{
exit(0);
}
}
/**********************************************************
*
* SUBROUTINE mouseMovement(void)
*
* Subroutine to handle mouse input
*
*********************************************************/
void mouseMovement(int x, int y) {
int diffx=x-lastx; //check the difference between the current x and the last x position
int diffy=y-lasty; //check the difference between the current y and the last y position
lastx=x; //set lastx to the current x position
lasty=y; //set lasty to the current y position
xrot += (float) diffy; //set the xrot to xrot with the addition of the difference in the y position
yrot += (float) diffx; //set the xrot to yrot with the addition of the difference in the x position
}
/**********************************************************
*
* SUBROUTINE display(void)
*
* This is our main rendering subroutine, called each frame
*
*********************************************************/
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // This clear the background color to dark blue
glMatrixMode(GL_MODELVIEW); // Modeling transformation
glPushMatrix();
glLoadIdentity(); // Initialize the model matrix as identity
glTranslatef(0.0f, 0.0f, -cRadius); // We move the object forward (the model matrix is multiplied by the translation matrix)
glRotatef(xrot,1.0,0.0,0.0); // Rotations of the object (the model matrix is multiplied by the rotation matrices)
glRotatef(yrot,0.0,1.0,0.0);
glTranslated(-xpos,0.0f,-zpos); //translate the screen to the position of our camera
if (objarray[0]->id_texture!=-1)
{
glBindTexture(GL_TEXTURE_2D, objarray[0]->id_texture); // We set the active texture
glEnable(GL_TEXTURE_2D); // Texture mapping ON
printf("Txt map ON");
}
else
glDisable(GL_TEXTURE_2D); // Texture mapping OFF
objarray[0]->render();
glPopMatrix();
glPushMatrix();
glTranslatef(5.0,0.0,-20.0);
glFlush(); // This force the execution of OpenGL commands
glutSwapBuffers(); // In double buffered mode we invert the positions of the visible buffer and the writing buffer
}
/**********************************************************
*
* The main routine
*
*********************************************************/
int main(int argc, char **argv)
{
// We use the GLUT utility to initialize the window, to handle the input and to interact with the windows system
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(screen_width,screen_height);
glutInitWindowPosition(0,0);
glutCreateWindow("Demo 1: To exit press ESC");
glutDisplayFunc(display);
glutIdleFunc(display);
glutReshapeFunc (resize);
glutPassiveMotionFunc(mouseMovement); //check for mouse movement
glutKeyboardFunc (keyboard);
init();
glutMainLoop();
return(0);
}
Example obj from blender, cube.obj:
# Blender v2.66 (sub 1) OBJ File: 'cube.blend'
# www.blender.org
mtllib cube.mtl
g Cube
v 1.000000 0.665869 -1.000000
v 1.000000 0.665869 1.000000
v -1.000000 0.665869 1.000000
v -1.000000 0.665869 -1.000000
v 1.000000 2.665869 -0.999999
v 0.999999 2.665869 1.000001
v -1.000000 2.665869 1.000000
v -1.000000 2.665869 -1.000000
vt 0.000000 0.334353
vt 0.332314 0.333333
vt 0.333333 0.665647
vt 1.000000 0.001019
vt 0.998981 0.333333
vt 0.666667 0.332314
vt 1.000000 0.665647
vt 0.667686 0.666667
vt 0.334353 0.666667
vt 0.333333 0.334353
vt 0.666667 0.665647
vt 0.333333 0.332314
vt 0.001020 0.333333
vt 0.332314 0.000000
vt 0.333333 0.001019
vt 0.665647 0.000000
vt 0.001019 0.666667
vt 0.667686 0.000000
vt 0.666667 0.334353
vt 0.665647 0.333333
vt 0.000000 0.001020
vt 0.334353 0.333333
vn 0.000000 -1.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 1.000000 -0.000000 0.000001
vn -0.000000 -0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
usemtl Material
s off
f 1/1/1 2/2/1 3/3/1
f 5/4/2 8/5/2 7/6/2
f 1/5/3 5/7/3 6/8/3
f 2/9/4 6/10/4 3/11/4
f 3/12/5 7/13/5 4/14/5
f 5/15/6 1/16/6 4/6/6
f 4/17/1 1/1/1 3/3/1
f 6/18/2 5/4/2 7/6/2
f 2/19/7 1/5/7 6/8/7
f 6/10/4 7/20/4 3/11/4
f 7/13/5 8/21/5 4/14/5
f 8/22/6 5/15/6 4/6/6
Your OBJ loader doesn't even attempt to parse MTL files.
You'll have to add MTL handling yourself.
You'll probably just want to support map_Kd.
Good luck.