3DS and normals

Started by
13 comments, last by BlackRyder 18 years, 7 months ago
some1 here knows if 3DS saves the compution of the face normals. and if 3ds does saves it in the file what is the CHUNK ID? 10x in Advance BlackRyder
Advertisement
Don't know, 3ds is such an old format, so im not even shure they thought about it then, maybe they added it on later, in witch case older models might not have it.
It might be worth to check out http://www.wotsit.org/ just to be shure.
Either way, it's probobly better if you calcylate them yourself at load time.
It's fast, easy and you might even get better results if you do it right.
i am computing the normals at loading but i got some crappy results maybe it worth if someone here could check it out for me:

calculating a face normal:

inline Vector3D Object::computeFaceNormal(const Face &face){	Vector3D u(vertex_list.at(face[0]),vertex_list.at(face[1]));	Vector3D v(vertex_list.at(face[0]),vertex_list.at(face[2]));	return u.crossProduct(v);}Vector3D Vector3D::crossProduct(const Vector3D &v){	Vector3D result;	result.x = (y*v.z) - (z*v.y);	result.y = (z*v.x) - (x*v.z);	result.z = (x*v.y) - (y*v.x);	return result;}


looks ok, but i can't be shure.
a few things can happen to create crappy results.
1. the normal can get inverted
2. the normal might have the wrong magnitude

So normalize your normals to a magnitude of 1 or -1 depending on witch one looks the best.

But this will only get you half way, the normals will make your models look faceted.

So to make the normals smoother you must blend your normals with it's neighbours.
For every vertic on every face you test the facenormal with every neighbouring faces facenormal using a dot product.
if the result is larger than 0.71 then add that normal to the vertic normal.
And finaly normalize all the normals one last time to a magnitude of 1.
your last algorithem is like O(n^2) you test every vertic to check the whole
conectivity.
isnt it faster to produce the normal list offline?
and load it when ever you need it...
it's allways faster to do that.
it's allways better to have your own format,instead of using another one that doesn't contain all the stuff you need.

But i did run this code once for every frame, it actuarly worked fairly well, but it didn't run as fast is i wanted to.
take a look at the code and comment it

Vector3D Object::computeVertexBlendedNormal(unsigned int vertexNum,vector<Vector3D> &facesNormalList){	Face face;					//Holds the current tested faces    unsigned int faceNum;		//Holds the current face number that we test.	Vector3D vertexNormal;		//Holds the result blended normal	Vector3D faceNormal;		//Holds the current face normal	Vector3D blendNormal;		//Holds the result blended normal	bool first=true;			//Holds indication if this is the first face that hold the vertex		for(faceNum=0;faceNum<face_list.size();faceNum++)	{		face = face_list.at(faceNum);		//if this is a neighbor face		if((face[0]==vertexNum)||(face[1]==vertexNum)||(face[2]==vertexNum))		{			faceNormal = facesNormalList.at(faceNum);			if(first)			{                                blendedNormal = faceNormal;				vertexNormal = faceNormal;				first = false;			}			else			{				if(vertexNormal.dotProduct(faceNormal)>=0.71)				{					blendNormal =vertexNormal+faceNormal;				}			}		}	}	blendNormal.normalize();	return blendNormal;}


[Edited by - BlackRyder on September 11, 2005 5:32:50 AM]
um more like this perhaps

note: currentfaceNum should be the current face this normal is attatched to, this is important.
Allso remember that each face has 4 normals, the face normal and the individual vertex normals, witch are the blended ones, i cant tell if you have it this way, but you should.

Vector3D Object::computeVertexBlendedNormal(unsigned int currentfaceNum,unsigned int vertexNum,vector<Vector3D> &facesNormalList){	Face face;					//Holds the current tested faces    unsigned int faceNum;		//Holds the current face number that we test.	Vector3D vertexNormal;		//Holds the result blended normal	Vector3D faceNormal;		//Holds the current face normal	Vector3D blendNormal;		//Holds the result blended normal	bool first=true;		//Holds indication if this is the first face that hold the vertexvertexNormal = facesNormalList.at(currentfaceNum);	for(faceNum=0;faceNum<face_list.size();faceNum++)	{		face = face_list.at(faceNum);		//if this is a neighbor face		if((face[0]==vertexNum)||(face[1]==vertexNum)||(face[2]==vertexNum))		{			faceNormal = facesNormalList.at(faceNum);			if(vertexNormal.dotProduct(faceNormal)>=0.71)			{				if(first)				{                              		blendedNormal = faceNormal;					first = false;				} else {				blendNormal +=faceNormal;				}			}		}	}	blendNormal.normalize();	return blendNormal;}
at first the facesNormalList has only the face normal
because at first each vertex has the same normal as its face normal
that is why i had the "first" indication because i take the first face that contains the vertex as the vertexNormal to work with and test according to it.

please correct me if i am wrong
Well, you have to use the normal it had first(the face normal), before you started testing, .

But that doesn't mean that it will be the one that you encounter first in the list, it might be the second one or third or whatever.

let me post some of my code(witch BTW is old and uggly), it might explain things a bit better.
POLS.p_data is the polygon structure where each p_data is a polygon.
.v_ref is the vertex references of the polygon.
.p_n is the face normal of the polygon.
.n[ ] is the vertex normals of the polygon.
there is allso a .v_uv, but it's not use here.
i,j,k are integers.


	float tn[3];  //temp normal storage	int num,vn;	i=0;	while(i<cfmdfile->POLS.p_num)		{		k=0;		while(k<3)		{			j=0;			num=0;			tn[0]=0;			tn[1]=0;			tn[2]=0;						// first find and add all normals who share this point to tn			while(j<cfmdfile->POLS.p_num)			{				if (cfmdfile->POLS.p_data[j].v_ref[0]==cfmdfile->POLS.p_data.v_ref[k]) 					{						if (dotproduct(cfmdfile->POLS.p_data[j].p_n,cfmdfile->POLS.p_data.p_n)>0.71f)						{							tn[0]+=cfmdfile->POLS.p_data[j].p_n[0];							tn[1]+=cfmdfile->POLS.p_data[j].p_n[1];							tn[2]+=cfmdfile->POLS.p_data[j].p_n[2];						}					}				if (cfmdfile->POLS.p_data[j].v_ref[1]==cfmdfile->POLS.p_data.v_ref[k]) 					{						if (dotproduct(cfmdfile->POLS.p_data[j].p_n,cfmdfile->POLS.p_data.p_n)>0.71f)						{							tn[0]+=cfmdfile->POLS.p_data[j].p_n[0];							tn[1]+=cfmdfile->POLS.p_data[j].p_n[1];							tn[2]+=cfmdfile->POLS.p_data[j].p_n[2];						}					}				if (cfmdfile->POLS.p_data[j].v_ref[2]==cfmdfile->POLS.p_data.v_ref[k]) 					{						if (dotproduct(cfmdfile->POLS.p_data[j].p_n,cfmdfile->POLS.p_data.p_n)>0.71f)						{							tn[0]+=cfmdfile->POLS.p_data[j].p_n[0];							tn[1]+=cfmdfile->POLS.p_data[j].p_n[1];							tn[2]+=cfmdfile->POLS.p_data[j].p_n[2];						}					}								j++;			}			//normalize the values			m=magnitude(tn);			tn[0]=tn[0]/m;			tn[1]=tn[1]/m;			tn[2]=tn[2]/m;					// Second, place it where it belongs			cfmdfile->POLS.p_data.n[k][0]=tn[0];			cfmdfile->POLS.p_data.n[k][1]=tn[1];			cfmdfile->POLS.p_data.n[k][2]=tn[2];						k++;		}			i++;		}

This topic is closed to new replies.

Advertisement