Sign in to follow this  
Mybowlcut

Reading in normals from an ASE

Recommended Posts

Hey. I've got a thing for Uni that requires me to read in normals for a model from an ASE file. The problem is that light is not looking that accurate on the model and I'm thinking I'm reading in the normals wrong. Here is my normal reading function:
void ASELoader::readVertexNormals(std::vector<Vertex3>& normals)
{
    char line[255];
    std::string currentLine = line;

	// read the file until we come to a close bracket
    m_modelFile.getline(line,255);
    currentLine = line;
    int i;
    float x,y,z;
    Vertex3 newNormal;
    std::map<int, Vertex3> mapNormals;

    if(m_lastFileName == "resources\\barrier.ASE")
    {
        bool b = true;
    }

	do
	{
		if (currentLine.find("*MESH_VERTEXNORMAL ")!= std::string::npos)
		{
			// read in the x,y,z components of the vertex
			// this line in the .ASE file is of the form (eg.):
			// *MESH_VERTEX    0	-20.1053	-18.7514	0.0000

			int displacement = (int)currentLine.find("*MESH_VERTEXNORMAL ") + 19;

			//sscanf(currentLine.substr(displacement).c_str(), "%d\t%f\t%f\t%f", &i, &x, &y, &z);
			sscanf_s(currentLine.substr(displacement).c_str(), "%d\t%f\t%f\t%f", &i, &x, &y, &z);

			newNormal.set(x,y,z);
		    //normals.push_back(newNormal);
                    mapNormals[i] = newNormal;
		}
		m_modelFile.getline(line,255);
		currentLine = line;

	} while (currentLine.find("}") == std::string::npos);

    normals.clear();
    normals.resize(mapNormals.size());
    for(std::map<int, Vertex3>::const_iterator m_it = mapNormals.begin();
    m_it != mapNormals.end(); ++m_it)
    {
        normals[m_it->first] = m_it->second;
    }
}

Here are the normals in the ASE file:
		*MESH_NORMALS {
			*MESH_FACENORMAL 0	0.0000	-0.0000	1.0000
				*MESH_VERTEXNORMAL 4	0.0000	0.0000	1.0000
				*MESH_VERTEXNORMAL 5	0.0000	-0.0000	1.0000
				*MESH_VERTEXNORMAL 7	0.0000	0.0000	1.0000
			*MESH_FACENORMAL 1	-0.0000	0.0000	1.0000
				*MESH_VERTEXNORMAL 7	0.0000	0.0000	1.0000
				*MESH_VERTEXNORMAL 6	-0.0000	0.0000	1.0000
				*MESH_VERTEXNORMAL 4	0.0000	0.0000	1.0000
			*MESH_FACENORMAL 2	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 0	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 1	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 5	0.0000	-1.0000	0.0000
			*MESH_FACENORMAL 3	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 5	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 4	0.0000	-1.0000	0.0000
				*MESH_VERTEXNORMAL 0	0.0000	-1.0000	0.0000
			*MESH_FACENORMAL 4	1.0000	0.0000	-0.0000
				*MESH_VERTEXNORMAL 1	1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 3	1.0000	0.0000	-0.0000
				*MESH_VERTEXNORMAL 7	1.0000	0.0000	0.0000
			*MESH_FACENORMAL 5	1.0000	-0.0000	0.0000
				*MESH_VERTEXNORMAL 7	1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 5	1.0000	-0.0000	0.0000
				*MESH_VERTEXNORMAL 1	1.0000	0.0000	0.0000
			*MESH_FACENORMAL 6	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 3	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 2	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 6	0.0000	1.0000	0.0000
			*MESH_FACENORMAL 7	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 6	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 7	0.0000	1.0000	0.0000
				*MESH_VERTEXNORMAL 3	0.0000	1.0000	0.0000
			*MESH_FACENORMAL 8	-1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 2	-1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 0	-1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 4	-1.0000	0.0000	0.0000
			*MESH_FACENORMAL 9	-1.0000	-0.0000	-0.0000
				*MESH_VERTEXNORMAL 4	-1.0000	0.0000	0.0000
				*MESH_VERTEXNORMAL 6	-1.0000	-0.0000	-0.0000
				*MESH_VERTEXNORMAL 2	-1.0000	0.0000	0.0000
		}

The light is green and is directly above the ground. If you look at the boundary blocks you can see that the lighting looks wrong: So can anyone tell me if my reading method looks right? Cheers.

Share this post


Link to post
Share on other sites
The reading looks okay at first glance.
Maybe you should post some rendering code. Maybe there's a mix up with indexes, or you display the stuff as quads or whatever.

Share this post


Link to post
Share on other sites
This is the lecturer's code, with some edits by me to meet the deliverables:

void Geometry::drawOpenGLVertexBufferObject()
{
// check that the buffer exists
if (!glIsBuffer(m_bufferVertList))
{
createOpenGLVertexBufferObject();
}

glPushMatrix();

// Enable shaders if any.
if(hasVertexShader()) CgSingleton::Get().ShaderAt(m_vertexShaderID, CG_GL_VERTEX).Enable();
if(hasFragmentShader()) CgSingleton::Get().ShaderAt(m_fragmentShaderID, CG_GL_FRAGMENT).Enable();

// model to parent transform
glTranslatef(m_relativePosition.x(), m_relativePosition.y(), m_relativePosition.z()); // translation

glRotatef(m_relativeOrientation.x(), 1, 0, 0); // Euler rotation
glRotatef(m_relativeOrientation.y(), 0, 1, 0);
glRotatef(m_relativeOrientation.z(), 0, 0, 1);

// end of model to parent transform

if(hasVertexShader())
{
cgGLSetStateMatrixParameter(cgGetNamedParameter(
CgSingleton::Get().ShaderAt(m_vertexShaderID, CG_GL_VERTEX).GetProgram(), "transform"),
CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
}

// set the geometry colour
glColor3f(m_colour.x(), m_colour.y(), m_colour.z());

// draw this geometry

// make the buffers current
glBindBuffer(GL_ARRAY_BUFFER, m_bufferVertList);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufferTriList);

// enable the drawing of vertex positions
glEnableClientState(GL_VERTEX_ARRAY);
// point to the vertex data
glVertexPointer(3,GL_FLOAT, 0, (float *)NULL);

// same for textures
if (!m_textureIndices.empty())
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,GL_FLOAT,0, (float *)NULL + (m_vertexCoordinates.size()* 3));
}

// activate the geometry texture
if (!m_materialFile.empty())
{
if (!glIsTexture(m_material))
{
m_material = TextureCreator::loadTexture(m_materialFile);
}
glEnable(GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, m_material);
}

if(!m_normalCoordinates.empty())
{
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, (float *)NULL + (m_normalCoordinates.size()* 3));
}

// draw using the buffer data
glDrawElements(GL_TRIANGLES, m_triangleIndices.size(), GL_UNSIGNED_INT, 0);

glDisable(GL_TEXTURE_2D);

// now draw the kids
for(vector<Geometry>::iterator child = m_children.begin(); child != m_children.end(); child++)
{
child->drawOpenGLVertexBufferObject();
}

/*cgGLDisableClientState(position);

CgSingleton::Get().DisableVertexProfile();*/


// Disable shaders if any.
if(hasVertexShader()) CgSingleton::Get().ShaderAt(m_vertexShaderID, CG_GL_VERTEX).Disable();
if(hasFragmentShader()) CgSingleton::Get().ShaderAt(m_fragmentShaderID, CG_GL_FRAGMENT).Disable();

glPopMatrix();
}



The texturedVertexShader:
struct OutputStructure
{
float4 position : POSITION;
float4 colour : COLOR;
float2 texCoord : TEXCOORD0;
};

OutputStructure vertexMain(
float4 position : POSITION,
float4 colour : COLOR,
float2 texCoord : TEXCOORD0,
float3 normal : NORMAL,
uniform float4x4 transform,
uniform float3 globalAmbientLight,
uniform float3 lightColour,
uniform float3 lightPosition,
uniform float3 eyePosition,
uniform float3 Ke,
uniform float3 Ka,
uniform float3 Kd,
uniform float3 Ks,
uniform float shininess)
{
OutputStructure OUT;

OUT.position = mul(transform, position);

float3 P = position.xyz;
float3 N = normal;

float3 emissive = Ke;

float3 ambient = Ka * globalAmbientLight;

float3 L = normalize(lightPosition - P);
float diffuseLight = max(dot(N, L), 0);
float3 diffuse = Kd * lightColour * diffuseLight;

float3 V = normalize(eyePosition - P);
float3 H = normalize(L + V);
float3 specularLight = pow(max(dot(N, H), 0), shininess);
if(diffuseLight <= 0)
{
specularLight = 0;
}
float3 specular = Ks * lightColour * specularLight;

OUT.colour.xyz = /*emissive + ambient +*/ diffuse /*+ specular*/;
OUT.colour.w = 1;

OUT.texCoord = texCoord;

return OUT;
}



The texturedFragmentShader:
struct OutputStructure
{
float4 colour : COLOR;
};


OutputStructure fragmentMain(
float4 colour : COLOR,
float2 texCoord : TEXCOORD0,
uniform sampler2D decal)
{
OutputStructure OUT;

OUT.colour = tex2D(decal, texCoord) + colour;

return OUT;
}


Share this post


Link to post
Share on other sites
Ok I'm desperate. Have tried some weird stuff but nothing has worked. I did manage to get the ground to display correctly with light, as its normals are all (0, 1.0, 0) so I could set them manually and know that they'd be correct, but for everything that's loaded from a ASE file (everything else), it's not working.

Here's my whole project ready to run in case anyone is kind enough to have a look.

Cheers.

Share this post


Link to post
Share on other sites
I usually draw the normals as simple lines. That makes it much easier to see what is going on :)

Share this post


Link to post
Share on other sites
It should help because you can usually see if the direction is wrong etc which should be an easy fix.

Share this post


Link to post
Share on other sites
Because of this function...
void Geometry::createOpenGLVertexBufferObject()
{
//
// Need to go from two arrays of indices (one for vertices, one for textures)
// to a single index array with each index for a texture and vertex coordinate
// this means expanding the array
//

vector<Vertex3> expandedVertices;
vector<Vertex3> expandedTextures;
vector<int> expandedIndices;
expandedVertices.clear(); expandedTextures.clear(); expandedIndices.clear();
int numberOfAddedVertices = 0;
bool insertingNew = true;

// loop through the existing indices
for (int i = 0; i< (int)m_triangleIndices.size(); i++)
{
// check if the vertex/texture pair is already inserted in the new list
for (int j=0; j< (int)numberOfAddedVertices; j++)
{
// check the vertex coordinate
if (m_vertexCoordinates[m_triangleIndices[i]] == expandedVertices[j])
{
if (!m_textureIndices.empty()) // if the model has textures, check them
{
if (m_textureCoordinates[m_textureIndices[i]] == expandedTextures[j])
{
// yes, this combination already exists, merely point to it
expandedIndices.push_back(j);
insertingNew = false;
break; // breaks out of the inner for loop
}
}
else
{
// yes, this combination already exists, merely point to it
expandedIndices.push_back(j);
insertingNew = false;
break; // breaks out of the inner for loop
}
}
}
if (insertingNew)
{
// if we are here it means there was no match, so insert the vertex and texture coordinates, and point to it
expandedVertices.push_back(m_vertexCoordinates[m_triangleIndices[i]]);
if (!m_textureCoordinates.empty())
{
expandedTextures.push_back(m_textureCoordinates[m_textureIndices[i]]);
}
expandedIndices.push_back(numberOfAddedVertices);
numberOfAddedVertices++;
}
else
{
insertingNew = true;
}

}

m_vertexCoordinates = expandedVertices;
m_triangleIndices = expandedIndices;
m_textureIndices = expandedIndices;
if (!m_textureIndices.empty())
{
m_textureCoordinates = expandedTextures;
}


// *****************************
// Create the vertex buffer
// *****************************

// get a buffer ID
glGenBuffers(1, &m_bufferVertList);
// make the buffer current
glBindBuffer(GL_ARRAY_BUFFER, m_bufferVertList);
// make space for the data
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*3*m_vertexCoordinates.size() +sizeof(GLfloat)*2*m_textureCoordinates.size()
+sizeof(GLfloat)*3*m_normalCoordinates.size(), NULL, GL_STATIC_DRAW);

// map the buffer to main memory
GLfloat *vertBuff = (GLfloat *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

// copy data into buffer

// vertices
int currentIndex = 0;
for each (Vertex3 currentVertex in m_vertexCoordinates)
{
vertBuff[currentIndex] = currentVertex.x();
vertBuff[currentIndex + 1] = currentVertex.y();
vertBuff[currentIndex + 2] = currentVertex.z();
currentIndex += 3;
}
// texture coordinates
if (!m_textureIndices.empty())
{
for each (Vertex3 currentTexture in m_textureCoordinates)
{
vertBuff[currentIndex] = currentTexture.x();
vertBuff[currentIndex + 1] = currentTexture.y();
currentIndex += 2;
}
}
bool usesLighting = hasVertexShader() && CgSingleton::Get().ShaderAt(m_vertexShaderID, CG_GL_VERTEX).UsesLighting();
if(!m_normalCoordinates.empty() && hasVertexShader() && usesLighting)
{
for each (Vertex3 currentNormal in m_normalCoordinates)
{
vertBuff[currentIndex] = currentNormal.x();
vertBuff[currentIndex + 1] = currentNormal.y();
vertBuff[currentIndex + 2] = currentNormal.z();
currentIndex += 3;
}
}

// unmap the bufffer
glUnmapBuffer(GL_ARRAY_BUFFER);


// *****************************
// Create the index buffer
// *****************************

// get a buffer ID
glGenBuffers(1, &m_bufferTriList);
// make the buffer current
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_bufferTriList);
// make space for the data
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_triangleIndices.size(), NULL, GL_STATIC_DRAW);

// map the buffer to main memory
GLuint *triBuff = (GLuint *)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);

// copy data into buffer
currentIndex = 0;
for each (int currentTriangleIndex in m_triangleIndices)
{
triBuff[currentIndex] = currentTriangleIndex;
currentIndex += 1;
}

// unmap the bufffer
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);

}



... not all models have the same amount of normals as they do vertices. I lack the knowledge to even understand why that would be, let alone know how to code to account for it. This is my drawNormals function:
void Geometry::drawNormals()
{
if(m_normalCoordinates.empty() || m_normalCoordinates.size() != m_vertexCoordinates.size())
{
return;
}

glPushMatrix();

glColor3f(1.0f, 0, 0);

size_t idx = 0;
for(std::vector<Vertex3>::const_iterator v_it = m_vertexCoordinates.begin();
v_it != m_vertexCoordinates.end(); ++v_it, ++idx)
{
glPushMatrix();

glBegin(GL_LINES);

Vertex3 vertexPos(*v_it);
Vertex3 normal(m_normalCoordinates[idx]);

glVertex3fv(vertexPos);
glVertex3fv(vertexPos + (normal * 2));

glEnd();

glPopMatrix();
}

glPopMatrix();
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this