First material in array applied to all objects, not just first

Started by
4 comments, last by capricorn 12 years, 8 months ago
Hi all

I'm writing yet another Wavefront object loader in C++. I've got nearly everything sorted except the materials. I load them from the .mtl file just fine, and when the values are printed, they show the correct numbers. However, when using glMaterialfv(), the first material applied is set to all the objects. Here's my draw function:

typedef struct
{
GLfloat Ka[3];
GLfloat Kd[3];
GLfloat Ks[3];
float Ns; // Specular (coeff)
}
Material;

typedef struct
{
int v1, v2, v3;
int vn1, vn2, vn3;
int vt1, vt2, vt3;
}
Face;

vector<Material> materials;
vector< vector<Face> > faces;

for(int g = 0; g < faces.size(); g++)
{
// Apply material for this material group
glMaterialfv(GL_FRONT, GL_AMBIENT, materials[g].Ka);
glMaterialfv(GL_FRONT, GL_DIFFUSE, materials[g].Kd);
glMaterialfv(GL_FRONT, GL_SPECULAR, materials[g].Ks);
glMaterialf(GL_FRONT, GL_SHININESS, materials[g].Ns);

glBegin(GL_TRIANGLES);

// Loop through faces
for(int f = 0; f < faces[g].size(); f++)
{
glNormal3f(normals[faces[g][f].vn1 - 1].x, normals[faces[g][f].vn1 - 1].y, normals[faces[g][f].vn1 - 1].z);
glVertex3f(vertices[faces[g][f].v1 - 1].x, vertices[faces[g][f].v1 - 1].y, vertices[faces[g][f].v1 - 1].z);

glNormal3f(normals[faces[g][f].vn2 - 1].x, normals[faces[g][f].vn2 - 1].y, normals[faces[g][f].vn2 - 1].z);
glVertex3f(vertices[faces[g][f].v2 - 1].x, vertices[faces[g][f].v2 - 1].y, vertices[faces[g][f].v2 - 1].z);

glNormal3f(normals[faces[g][f].vn3 - 1].x, normals[faces[g][f].vn3 - 1].y, normals[faces[g][f].vn3 - 1].z);
glVertex3f(vertices[faces[g][f].v3 - 1].x, vertices[faces[g][f].v3 - 1].y, vertices[faces[g][f].v3 - 1].z);
}

glEnd();
}


If the first elements of materials[] constitutes, for example, red then all subsequent material groups (the inner for() loop) are all red, even though printing the contents of the array directly prior to glMaterialfv() shows the correct values. Obviously, each object should have a different material as I have set in the .mtl file.

Please let me know if this makes no sense or you need more code/.obj/.mtl files and I'll amend my question. Thank you for any help you may be able to give.

James
http://www.jamwaffles.co.uk - my site - new look, faster loading, free wallpapers, tutorials and other pointless crap.
Advertisement
I can't much help you with the issue itself other than by advising you to check if "faces" array is constructed properly (that is, each element has some faces belonging to respective group, not just the first group containing all faces).

I just wanted to point out is that you have a lucky layout for your Material struct. Lucky because it doesn't present you with [s]memory corruption[/s] access violation or any other effect of the "undefined behavior". Vector parameters for glMaterialfv should be 4-element vectors, whereas yours are 3-element ones. Be safe :)
WBW, capricorn
I'm assuming you are in the process of writing this wavefront object loader/renderer, but here are some suggestions.


1) keep track of all groups and non-groups.
2) keep track of how many vertexes each face has, are you only going to be working with triangle faces?
3) keep track of which properties are enabled, and disable/enable what you need and then reset them back to the way they were before you rendered
4) reset anything you don't want after each group is rendered


Here's is my wavefront render function for groups, although not perfect, might help.




Render function for groups

void OpenGL::WavefrontObject::RenderGroups()
{
for (unsigned int g = 0; g < mesh->groups.size(); g++)
{

Group* currentGroup = mesh->groups[g];
for (unsigned int f = 0; f < currentGroup->faces.size(); f++)
{

if (currentGroup->material != NULL)
{
glBindTexture(GL_TEXTURE_2D, currentGroup->material->diffuseTexture.TextureId());

if (properties.useLighting)
{

glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float*)currentGroup->material->specularColor.RGBA());
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float*)currentGroup->material->diffuseColor.RGBA());
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float*)currentGroup->material->ambientColor.RGBA());

}
}

// determine what type needs to be rendered
if(currentGroup->faces[f]->verticesCount == 4)
{
glBegin(GL_QUADS);
}
else if(currentGroup->faces[f]->verticesCount == 3)
{
glBegin(GL_TRIANGLES);
}
else if(currentGroup->faces[f]->verticesCount == 2)
{
glBegin(GL_LINES);
}
else if(currentGroup->faces[f]->verticesCount == 1)
{
glBegin(GL_POINTS);
}
else
{
glBegin(GL_POLYGON);
}

for (unsigned int n = 0; n < currentGroup->faces[f]->verticesCount; n++)
{

// create the 3D vertexes, textures, and normals,
// if there is a texture to render then render it, otherwise render the color
if(currentGroup->material != NULL && currentGroup->faces[f]->vertexTextureIndices.size() > 0)
{
if (currentGroup->material->diffuseTexture.TextureId() != Texture::NO_TEXTURE)
{
glTexCoord2d(mesh->textureCoords[ currentGroup->faces[f]->vertexTextureIndices[n] - 1 ].x,
mesh->textureCoords[ currentGroup->faces[f]->vertexTextureIndices[n] - 1 ].y);
}
}
else if(currentGroup->faces[f]->vertexNormalIndices.size() > 0)
{
//glColor3d( properties.colorRed, properties.colorGreen, properties.colorBlue);
}

if(currentGroup->faces[f]->vertexNormalIndices.size() > 0)
{
glNormal3d(mesh->vertexNormals[ currentGroup->faces[f]->vertexNormalIndices[n] - 1 ].x * properties.scale, //x normal
mesh->vertexNormals[ currentGroup->faces[f]->vertexNormalIndices[n] - 1 ].y * properties.scale, //y normal
mesh->vertexNormals[ currentGroup->faces[f]->vertexNormalIndices[n] - 1 ].z * properties.scale); //z normal
}

glVertex3d(mesh->vertices[ currentGroup->faces[f]->vertexIndices[n] - 1 ].x * properties.scale, //x coordinate
mesh->vertices[ currentGroup->faces[f]->vertexIndices[n] - 1 ].y * properties.scale, //y coordinate
mesh->vertices[ currentGroup->faces[f]->vertexIndices[n] - 1 ].z * properties.scale); //z coordinate
}

glEnd();

}

}
}





Entire render function


bool OpenGL::WavefrontObject::RenderWavefront(Vector* position, Vector* rotation)
{
bool enabledDepthTest = true;
bool enabledTexture2D = true;
bool enabledLighting = true;
bool enabledColorMaterial = true;

if (mesh == NULL || position == NULL || rotation == NULL)
{
return false; // nothing is loaded yet
}

glPushMatrix(); // keep translation/rotation relative to this mesh

glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

if (!glIsEnabled(GL_DEPTH_TEST))
{
glEnable(GL_DEPTH_TEST);
enabledDepthTest = false;
}

if (!glIsEnabled(GL_TEXTURE_2D))
{
glEnable(GL_TEXTURE_2D);
enabledTexture2D = false;
}

if (!glIsEnabled(GL_LIGHTING))
{
glEnable(GL_LIGHTING);
enabledLighting = false;
}

if (!glIsEnabled(GL_COLOR_MATERIAL))
{
glEnable(GL_COLOR_MATERIAL);
enabledColorMaterial = false;
}

glTranslated(position->x, position->y, position->z);
glRotated(rotation->x, 1, 0, 0);
glRotated(rotation->y, 0, 1, 0);
glRotated(rotation->z, 0, 0, 1);

// GL_ONE_MINUS_SRC_ALPHA assumes the alpha is subtracting from 1
glColor3f(1, 1, 1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);



RenderGroups();
RenderNonGroups();



glBindTexture(GL_TEXTURE_2D, 0); // unbind any texture so other objects are not screwed up

if (!enabledDepthTest) glDisable(GL_DEPTH_TEST);
if (!enabledTexture2D) glDisable(GL_TEXTURE_2D); // disable texturing so it does not interfere with coloring objects
if (!enabledLighting) glDisable(GL_LIGHTING);
if (!enabledColorMaterial) glDisable(GL_COLOR_MATERIAL);

glPopMatrix(); // undo relative translation/rotation and go back to original state

return true;
}
/ Visual Studios 2010 / Codeblocks 10.05 / Windows 7 / Ubuntu 10.10 / - I might be wrong

I can't much help you with the issue itself other than by advising you to check if "faces" array is constructed properly (that is, each element has some faces belonging to respective group, not just the first group containing all faces).

I just wanted to point out is that you have a lucky layout for your Material struct. Lucky because it doesn't present you with [s]memory corruption[/s] access violation or any other effect of the "undefined behavior". Vector parameters for glMaterialfv should be 4-element vectors, whereas yours are 3-element ones. Be safe :)


Thank you for your input capricorn. I've tried adding a fourth element to each material property with a value of 1.0f, but that made no change whatsoever. Could it be because I'm storing the red channel in Kd[0], green in Kd[1], etc instead of starting from 1?

Could it also be that each value is 0 not 0.0 (when printed), meaning it's casted to an int, for example?
http://www.jamwaffles.co.uk - my site - new look, faster loading, free wallpapers, tutorials and other pointless crap.
Fixed it! Turns out I was overwriting the vertex information in the first element of faces[], so the first material group encompassed all vertices. I just used a material index and WHUMP it works. Thank you for your help anyway, everyone!
http://www.jamwaffles.co.uk - my site - new look, faster loading, free wallpapers, tutorials and other pointless crap.

Thank you for your input capricorn. I've tried adding a fourth element to each material property with a value of 1.0f, but that made no change whatsoever. Could it be because I'm storing the red channel in Kd[0], green in Kd[1], etc instead of starting from 1?


No, 0 is the first index. My advice about 4 elements was purely for code correctness, it has nothing to do with your actual issue.


Could it also be that each value is 0 not 0.0 (when printed), meaning it's casted to an int, for example?
[/quote]

No, it normally prints like that if float was initialized with 0. It does print actual decimal places when they're there, does it?

[s]Have you checked that anything besides faces[0] has something stored? If I understood your problem, all faces are drawn with the first material, right? From your code, the only reason I can suggest is that all faces are stored in faces[0].[/s]
WBW, capricorn

This topic is closed to new replies.

Advertisement