Question regarding Maya Obj Normals


#1

I am writing a cpp/cg application that involves reading obj files exported from maya. In order to draw the imported model via glDrawElement (i.e. allows only one indices array) I rearranged the normal and uv arrays as follows:

void Obj::rearrangeData()
{
	vector<float> tmpUV;
	vector<float> tmpN;

	int size = faces.size();
	int normalSize = normal.size();
	int uvSize = uv.size();
	int psize = pos.size();

	tmpUV.assign(uv.begin(), uv.end());
	tmpN.assign(normal.begin(), normal.end());

	normal.clear();
	normal.resize(psize);
	uv.clear();
	uv.resize(psize*2/3);
	

	for (int i = 0; i < size; i++) {
		for (int j = 0; j < 3; j++) {
			
			int iP = ((ObjFace)faces[i]).v[j].indexP - 1;
			int iN = ((ObjFace)faces[i]).v[j].indexN - 1;
			int iUV = ((ObjFace)faces[i]).v[j].indexUV - 1;
			
			indices.push_back(iP);
			
			uv[iP*2] = tmpUV[iUV*2];
			uv[iP*2+1] = tmpUV[iUV*2+1];

			normal[iP*3] = tmpN[iN*3];
			normal[iP*3+1] = tmpN[iN*3+1];
			normal[iP*3+2] = tmpN[iN*3+2];
		}
	}
	//checkNormal(tmpN); 
}

However, when I tried to draw the model in my own application, the normals are way off.

Screenshot with lighting and texture on:

Screenshot with only texture (it implies the UVs are correct):

Screenshot for the diffuse lighting map only:

Here’s the thing that really confuses me:

In order to test the rearranged data, I write the exact model data that I used to draw the geometry back to another obj file, import the file into Maya, and the model, normals and UV’s just look correct.

I also tested the DrawGeometry function in my application on other simple geometry such as a sphere, it works fine, i.e. the cg and opengl part should be OK. I suspect the problem is due to the rearrangement of Normals or something like that.


#2

You know there will be more normals than points right? And you know that you cannot simply use the same point indices for normal indices right? This just exapands everything out to use a flattened array of data and then glDrawArrays. If you wish to use glDrawElements, then you will have to generate entirely new data arrays and new indices for the vertices.


void Obj::rearrangeData(vector<float>& outputVertexBuffer)
{
	for (int i = 0; i < faces.size(); i++) {
		for (int j = 0; j < 3; j++) {
			
			int iP = ((ObjFace)faces[i]).v[j].indexP - 1;
			int iN = ((ObjFace)faces[i]).v[j].indexN - 1;
			int iUV = ((ObjFace)faces[i]).v[j].indexUV - 1;
			
			outputVertexBuffer.push_back( pos[ iP*3 + 0 ] );
			outputVertexBuffer.push_back( pos[ iP*3 + 1 ] );
			outputVertexBuffer.push_back( pos[ iP*3 + 2 ] );
			
			outputVertexBuffer.push_back( normal[ iN*3 + 0 ] );
			outputVertexBuffer.push_back( normal[ iN*3 + 1 ] );
			outputVertexBuffer.push_back( normal[ iN*3 + 2 ] );

			outputVertexBuffer.push_back( uv[ iUV*2 + 0 ] );
			outputVertexBuffer.push_back( uv[ iUV*2 + 1 ] );			
		}
	}
}

Obj someObj;

// load stuff

vector<float> outputVertexBuffer

Obj.rearrangeData(outputVertexBuffer);

glVertexPointer(3,GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[0]));
glNormalPointer(GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[3]));
glTexCoordPointer(2,GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[6]));


glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glDrawArrays(GL_TRIANGLES,0,outputVertexBuffer.size()/8);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

To do an indexed set of elems you’ll have to generate the new indices, ie :


struct VInf
{
  int n;
  int t;
  int index;
  VInf(int _n,int _t,int _index) : n(_n), t(_t), index(_index) {}
  VInf(const VInf& vi) : n(vi.n), t(vi.t), index(vi.index) {}
};
typedef std::vector<VInf> VInfArray;

class VInfMap : public std::vector<VInfArray> 
{
  int maxindex;
public:

  VInfMap() : std::vector<VInfArray>(), maxindex(0) {}

  int insert_elem(int v,int n,int t) 
  {
	VInfArray& element_info = (*this)[v];
	for(int i=0;i!=element_info.size();++i)
	{
	  if( n == element_info[i].n && t == element_info[i].t )
	  {
		  return element_info[i].index;
	  }
	}
	element_info.push_back( VInf(n,t,maxindex) );
	return maxindex++;
  }
}



void Obj::rearrangeData(vector<float>& outputVertexBuffer,vector<int>& outputIndexBuffer)
{
  VInfMap temp;
  
  outputIndexBuffer.resize( faces.size()*3 );
  
  temp.resize(pos.size()/3);
  
  vector<int>::iterator index = outputIndexBuffer.begin();
 
	for (int i = 0; i < faces.size(); i++) {
		for (int j = 0; j < 3; j++) {
			
			int iP = ((ObjFace)faces[i]).v[j].indexP - 1;
			int iN = ((ObjFace)faces[i]).v[j].indexN - 1;
			int iUV = ((ObjFace)faces[i]).v[j].indexUV - 1;
			
			*index = temp.insert_elem( iP, iN, iUV );
			if((*index)>=(outputVertexBuffer.size()/8))
			{
			  outputVertexBuffer.push_back( pos[ iP*3 + 0 ] );
			  outputVertexBuffer.push_back( pos[ iP*3 + 1 ] );
			  outputVertexBuffer.push_back( pos[ iP*3 + 2 ] );
			
			  outputVertexBuffer.push_back( normal[ iN*3 + 0 ] );
			  outputVertexBuffer.push_back( normal[ iN*3 + 1 ] );
			  outputVertexBuffer.push_back( normal[ iN*3 + 2 ] );

			  outputVertexBuffer.push_back( uv[ iUV*2 + 0 ] );
			  outputVertexBuffer.push_back( uv[ iUV*2 + 1 ] );			
			}
		}
	}
}



 Obj someObj;
 
 // load stuff
 
 vector<float> outputVertexBuffer
  vector<int> outputIndexBuffer
 
 Obj.rearrangeData(outputVertexBuffer,outputIndexBuffer);
 
 glVertexPointer(3,GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[0]));
 glNormalPointer(GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[3]));
 glTexCoordPointer(2,GL_FLOAT,8*sizeof(float),&(outputVertexBuffer[6]));
 
 
 glEnableClientState(GL_VERTEX_ARRAY);
 glEnableClientState(GL_NORMAL_ARRAY);
 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
 glDrawElements(GL_TRIANGLES,0,outputIndexBuffer.size()/3,&(outputIndexBuffer[0]));
 
 glDisableClientState(GL_VERTEX_ARRAY);
 glDisableClientState(GL_NORMAL_ARRAY);
 glDisableClientState(GL_TEXTURE_COORD_ARRAY);


#3

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.