Jump to content
  • Advertisement
Sign in to follow this  
bijan311

Loading obj file question

This topic is 2498 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

At loaded an obj file with only position and index information, which was pretty easy, but now I'm trying to load a model with a texture and I can''t figure out how to load it. Here's the file I'm trying to load



# Blender v2.61 (sub 0) OBJ File: ''
# www.blender.org
mtllib test.mtl
o Cube
v 0.084387 1.041998 1.380985
v 1.787577 -0.810491 2.263559
v 1.972670 -0.890549 -0.737845
v 0.846381 1.450942 -0.422381
v -1.606249 0.376086 0.492014
v -1.123801 -1.126540 0.529063
v -0.443052 -0.778175 -1.432912
v -0.944857 0.871989 -1.310104
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
usemtl Material_texture.bmp
s off
f 1/1 2/2 3/3 4/4
f 5/1 8/2 7/3 6/4
f 1/1 5/2 6/3 2/4
f 2/1 6/2 7/3 3/4
f 3/1 7/2 8/3 4/4
f 5/1 1/2

4/3 8/4


My questions are, why are there only 4 texture coordinates, instead of one per position, and why are there two numbers in the index data (e.g. 1/1, instead of just 1).

I hope that makes sense, and thanks.

Share this post


Link to post
Share on other sites
Advertisement
The rows that start with f are position/UV index pairs.

So, the 1/1 line refers to position at index 1 (v [color="#006666"]0.084387 [color="#006666"]1.041998 [color="#006666"]1.380985) and texture coordinate ([color="#000000"]vt [color="#006666"]0.000000 [color="#006666"]0.000000).

[color="#006666"][color="#006666"]Note also that obj files are indexed starting from 1.

Share this post


Link to post
Share on other sites

The rows that start with f are position/UV index pairs.

So, the 1/1 line refers to position at index 1 (v [color="#006666"]0.084387 [color="#006666"]1.041998 [color="#006666"]1.380985) and texture coordinate ([color="#000000"]vt [color="#006666"]0.000000 [color="#006666"]0.000000).

Alright, but my understanding of vertices (at least in directx) is that each vertex can only have one texture coordinate, so how can I implement this in the code?


[color="#006666"][color="#006666"]Note also that obj files are indexed starting from 1.


So subtract 1 before putting the index into the buffer?

Thanks for your response

Share this post


Link to post
Share on other sites
Yes, you noticed that Direct3D and the Wavefront Object format handle meshes differently.

You need to parse the vertex attributes into separate buffers, then when you parse a face, you construct a vertex by using the indexes to index the attributes in the buffers. Next, you have to see if this constructed vertex already exists in your final vertex buffer. If it doesn't exist, you add it to the vertex buffer and add it's index to the index buffer, then you use an associative container to associate that vertex with it's index. If it does exist already, then you add nothing to the vertex buffer and use your associative container to look up the index of that vertex and add that to the index buffer.

You may also need to handle triangulation, or simply make it a requirement that all models be triangulated. I also found that I needed to invert my texture coordinates and it uses counter clockwise order for front facing polygons. I'm not sure if that's because of blender or if that's just a part of the object format.

Share this post


Link to post
Share on other sites
And one more thing if you don't mind, since I'm new to this I was wondering if this was a good way to load a 3d model. Thanks



char input[10] = "";
vector<Vertex> vertexVector;
vector<D3DXVECTOR3> vertexPos;
vector<D3DXVECTOR2> texCoord;
vector<DWORD> indexVector;
vector<DWORD> texCoordIndex;

//Read the file
while (input[0] != 'e')
{
//Get the file input
file>> input;

//If it is a texture coordinate put it in the texCoord vector
if(input[0] == 'v' && input[1] == 't')
{
D3DXVECTOR2 tex;

//U coordinate
file>> input;
tex.x = (float)atof(input);
//V coordinate
file>> input;
tex.y = (float)atof(input);

texCoord.push_back(tex);
}
//If it is a vector position, put it in the pos vector
else if(input[0] == 'v')
{
D3DXVECTOR3 pos;

//X coordinate
file>> input;
pos.x = (float)atof(input);
//Y coordinate
file>> input;
pos.y = (float)atof(input);
//Z coordinate
file>> input;
pos.z = (float)atof(input);

vertexPos.push_back(pos);
}
//If it is a index
else if(input[0] == 'f')
{
//There a three indices per 'f' and it is index/texture index so input[0] is the index and input[2] is the texture index
file>> input;
indexVector.push_back(atoi(&input[0]) - 1);
texCoordIndex.push_back(atoi(&input[2]) - 1);
file>> input;
indexVector.push_back(atoi(&input[0]) - 1);
texCoordIndex.push_back(atoi(&input[2]) - 1);
file>> input;
indexVector.push_back(atoi(&input[0]) - 1);
texCoordIndex.push_back(atoi(&input[2]) - 1);
}
}
//Put the information into the vertexVector
for(int i = 0; i < indexVector.size(); i++)
{
Vertex vertex;

vertex.Pos = vertexPos[indexVector];
vertex.Tex = texCoord[texCoordIndex];

vertex.Color = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);

vertexVector.push_back(vertex);
}

int iNumVerts = vertexVector.size();
int iNumIndices = indexVector.size();

//Create the mesh
ID3DX10Mesh* pTempMesh;
D3DX10CreateMesh(g_pGame->GetDevice(), layout, iNumElements, "POSITION", iNumVerts, iNumIndices, D3DX10_MESH_32_BIT, &pTempMesh);
pTempMesh->SetVertexData(0, &vertexVector);
pTempMesh->SetIndexData(&indexVector, iNumIndices);

//optimize the vector
pTempMesh->GenerateAdjacencyAndPointReps(0.001f);
pTempMesh->Optimize(D3DX10_MESHOPT_ATTR_SORT | D3DX10_MESHOPT_VERTEX_CACHE, 0, 0);

pTempMesh->CommitToDevice();

Share this post


Link to post
Share on other sites
A few things to be weary of:
1) Not all obj files follow that pattern precisely, depending on who/what created them. They can forgo the texture coordinate stream, or even have UVW instead of just UV.

2) Using std::vector without predeterming size can bloat your memory pretty quickly. From memory, default behaviour is to reallocate double in size each time the capacity is blown. So if you have 65537 (2^16+1) floats, it will jump to 131072 floats.

3) Parsing a text file is slower than loading a binary blob of your vertex data. In the future, when you are up for it and load-times become a problem, consider writing a step that loads the obj and saves out the resulting mesh in a way that can be read straight from disk without parsing.

Share this post


Link to post
Share on other sites
I use an unordered_set so I can check to see if the vertex I'm constructing already exists, to prevent duplicates. Then I use an unordered_map to associate vertex to it's index.

This code is rough and makes a lot of assumptions, but it seems to work.


int main(int argc, char* argv[])
{
if(argc < 2)
return 0;

std::ifstream InFile(argv[1]);

if(!InFile)
return 0;

std::vector<vec3> positions;
std::vector<vec3> normals;
std::vector<vec2> texcoords;

std::unordered_set<Vertex> vertex_set;
std::unordered_map<Vertex, unsigned int> index_map;

std::vector<Vertex> vertices;
std::vector<unsigned int> indices;

std::string cmd;

for(;;)
{
InFile >> cmd;
if(!InFile)
break;

if(cmd == "v")
{
float x, y, z;
InFile >> x >> y >> z;
positions.push_back(vec3(x, y, z));
}
else if(cmd == "vt")
{
float u, v;
InFile >> u >> v;
texcoords.push_back(vec2(1.0f - u, 1.0f - v));
}
else if(cmd == "vn")
{
float x, y, z;
InFile >> x >> y >> z;
normals.push_back(vec3(x, y, z));
}
else if(cmd == "f")
{
int pos, norm, tex;

for(int i = 0; i < 3; ++i)
{
Vertex vertex;

InFile >> pos;
vertex.position = positions[pos - 1];

if(InFile.peek() == '/')
{
InFile.ignore();

if(InFile.peek() != '/')
{
InFile >> tex;
vertex.texcoord = texcoords[tex - 1];
}
if(InFile.peek() == '/')
{
InFile.ignore();

InFile >> norm;
vertex.normal = normals[norm - 1];
}
}

auto it = vertex_set.find(vertex);
if(it == vertex_set.end())
{
vertex_set.insert(vertex);
vertices.push_back(vertex);
int index = vertices.size() - 1;
indices.push_back(index);
index_map[vertex] = index;
}
else
{
indices.push_back(index_map[vertex]);
}
}
}
else
{
//nothing
}
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!