Jump to content

  • Log In with Google      Sign In   
  • Create Account

How to calculate normals for a trianglemesh?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
17 replies to this topic

#1 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 16 April 2014 - 06:03 AM

Hi, I am creating a meshviewer using jme3 I read the meshes data from a file that has no normals data. So I need to create them with my code but I don´t know how can I create the normals for a trianglesmesh from my java code in jmonkey.



Sponsor:

#2 DekuTree64   Members   -  Reputation: 986

Like
0Likes
Like

Posted 16 April 2014 - 08:57 AM

For polygons, cross product two of the edges. Like (v2-v1)x(v3-v2). Then normalize it.
 

Once you have all the face normals, you can calculate vertex normals by averaging all the face normals that the vertex belongs to. Easiest way to do it is to zero out all the vertex normals, then loop over all the faces and add the face normal into each of its vertices' normal. Then loop over all the vertex normals and normalize them.



#3 Buckeye   Crossbones+   -  Reputation: 6386

Like
0Likes
Like

Posted 16 April 2014 - 09:21 AM


For polygons, cross product two of the edges. Like (v2-v1)x(v3-v2). Then normalize it.

If the winding order for the triangle is v1, v2, v3 - the crossproduct should be (v2-v1) x (v3-v1) to get the correct direction, assuming that clockwise winding order implies left-hand rule crossproducts and counter-clockwise order implies right-hand rule crossproducts.

 

Also, if the mesh faces do not share vertices - i.e., a vertex is used in only one face - the algorithm DekuTree64 will work, but comprises a lot of extra calcs. In this case, simply calculate the face normal via the crossproduct mentioned and set the normal for each of the 3 face vertices to that face normal.


Edited by Buckeye, 16 April 2014 - 09:24 AM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#4 TheChubu   Crossbones+   -  Reputation: 4802

Like
0Likes
Like

Posted 16 April 2014 - 10:30 AM


In this case, simply calculate the face normal via the crossproduct mentioned and set the normal for each of the 3 face vertices to that face normal.

Wouldn't that produce ugly lighting results?

 

EDIT: For anything that isn't a cube that is.


Edited by TheChubu, 16 April 2014 - 10:31 AM.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

 

My journals: dustArtemis ECS framework and Making a Terrain Generator


#5 DiegoSLTS   Members   -  Reputation: 1930

Like
0Likes
Like

Posted 16 April 2014 - 11:42 AM

 


For polygons, cross product two of the edges. Like (v2-v1)x(v3-v2). Then normalize it.

If the winding order for the triangle is v1, v2, v3 - the crossproduct should be (v2-v1) x (v3-v1) to get the correct direction, assuming that clockwise winding order implies left-hand rule crossproducts and counter-clockwise order implies right-hand rule crossproducts.

 

Also, if the mesh faces do not share vertices - i.e., a vertex is used in only one face - the algorithm DekuTree64 will work, but comprises a lot of extra calcs. In this case, simply calculate the face normal via the crossproduct mentioned and set the normal for each of the 3 face vertices to that face normal.

 

If he's reading the mesh from a file, the only way to know if a face is not connected to other faces is going through all the faces and checking if any of it's vertices are in the face you're interested. And if you check every face you'll have to iterate over the full mesh a lot of times.

 

To me it looks like more work, with DekuTree64 method you must go over the vertices once (to set the vertex normals to 0), over the faces only once (to compute the face normal and add it to each vertex of that face), and then over the vertices once again (to normalize the vertex normal).



#6 Buckeye   Crossbones+   -  Reputation: 6386

Like
0Likes
Like

Posted 16 April 2014 - 12:15 PM


Wouldn't that produce ugly lighting results?

EDIT: For anything that isn't a cube that is.

Cubes aren't the only thing with "flat" faces. smile.png He didn't mention whether the mesh in question was organic, a mechanical object, etc. Buildings, platforms, etc., look much better with flat faces.

 

 

 


If he's reading the mesh from a file, the only way to know if a face is not connected to other faces is going through all the faces

Not necessarily. If a mesh is loaded for a specific purpose in a scene, etc., one would know the characteristics of the mesh. If the mesh is known to have "flat" faces, I'd skip the extra calcs. That's why I qualified my comment with "if the mesh faces do not share vertices.."


Edited by Buckeye, 16 April 2014 - 12:17 PM.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.


#7 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 17 April 2014 - 05:43 AM

The mesh in question is a human face. As i am using jmonkey i would like to know if it has any method to make all this calculations or if is it another java api to make this calculations?



#8 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 18 April 2014 - 03:10 AM

I made what you told, calculate the face´s normals and then lopp throw the vertex to calculate the vertex normals. My result is very confusing, despite i set facecullmode to off, this is my render result:

 

Any clue about the problem?could be something with the normals calculation?l5jn.jpg



#9 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 18 April 2014 - 07:45 AM

Reviewing my code and the results I got I think that my problem is that some os the faces are clockWise and other counterclockwise, as I didn´t know how to determine if a face  is clockwise or counterclockwise I calculated the face's normals like this:

//Calculate the face normal, true for clockwise false counterclockwise(not confirmed)
		static Vector3f calculateFaceNormal(Vector3f v1,Vector3f v2,Vector3f v3, Boolean isClockWise){
			//cross product
			Vector3f crossProduct;
			if(isClockWise)
				crossProduct=v2.subtract(v1).mult(v3.subtract(v2));
			else
				crossProduct=v2.subtract(v1).mult(v3.subtract(v1));
			
			//normalize
			return crossProduct.normalize();
		}

So I think that I must change this code in a way to know if a face is clockwise or counterclockwise and then choose the right crossProduct for it. but I don´t know how to determine that.



#10 DiegoSLTS   Members   -  Reputation: 1930

Like
0Likes
Like

Posted 18 April 2014 - 08:39 AM

The file must be saved using some convention, every face should be stored clockwise or counterclockwise. But I'm guessing that when you open the file in another program it shows ok, right? Can you share the file? I'd like to take a look of it. Have you tried other files?



#11 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 18 April 2014 - 10:27 AM

The file is from a game and you need to understand it to extract the data so if i put the file you couldn´t see nothing clearly, what I can do is extract the vertex positions as binary file telling you the dataformat, and also extract the trianglestrip in another binaryfile.Is it right for you?

 

The file in its game shows the mesh correctly



#12 TheChubu   Crossbones+   -  Reputation: 4802

Like
0Likes
Like

Posted 18 April 2014 - 10:58 AM

Hmm... That's like the exact result I had when I imported .obj files that weren't triangulated. Basically, half the indices were missing.


"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

 

My journals: dustArtemis ECS framework and Making a Terrain Generator


#13 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 18 April 2014 - 11:04 AM

Here is the triangle strip binary file: http://www48.zippyshare.com/v/72608354/file.html

Here the vertexPositions binary file: http://www48.zippyshare.com/v/34761531/file.html
 
Vertex definition: each vertex 8 bytes: 2bytes x, 2 bytes y, 2bytes z, 2 bytes unknown
Triangle strip: Uint16


#14 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 18 April 2014 - 02:09 PM

Here i left a custom file that i made with the info of the mesh, is a string format file, easy to read in text editor: http://www75.zippyshare.com/v/45873494/file.html



#15 DiegoSLTS   Members   -  Reputation: 1930

Like
0Likes
Like

Posted 18 April 2014 - 02:58 PM

Oh, I thought you were opening a file and reading every vertex and face directly from the file, but it sounds like the file stores the information in a different way and the you make a vertices and faces list, right?

 

I was looking at the .m3d file you uploaded, and if you look at two consecutive faces you'll see that most of the times one is clockwise and the other is counterclockwise:

 

For example:

13 14 15 <- if this is counterclockwise
14 15 17 <- this would be clockwise

 

Since those 2 faces share a side (from vertex number 14 to vertex number 15) the second line should be "15 14 17" instead. Maybe the code that reads that model and generates the list of vertices and faces is doing something wrong. Before you posted a triangle strip, does the original file specify the way to draw it? I guess maybe it stores something like "13 14 15 17" and the file loader makes a face for 13 14 15 and the other for 14 15 17 when the second face should be 15 14 17. If you use the vertex directly to draw a triangle strip, OpenGL makes that swap for you.

 

As for the question you posted before about knowing if a face is defined clockwise or counterclockwise, unless the file format stores something that specifies it there's no way to tell. In a three dimensional space, a face is defined clockwise AND counterclockwise, it's relative to where the camera is standing. What you could do (with A LOT of work) is check if a normal is pointing outside or inside of the model, but it's not easy and for sure is not trivial.



#16 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 19 April 2014 - 06:08 AM

I think you are right, the triangleList was created by me with my own code deleteing degenerated triangles, and not changing the order of the vertices in the triangles.

I was trying now triStrip methos of jme3 to build the triangleStrip but i think i can´t use because it optimize the triangleList making that some vertices doesn´t belong to any face and when I import the mesh to blender i get errors.

So to test it your theory is right i need to create a correct triangleList to see if the mesh is rendered ok.

 

i have to two options, find an api that create a triangleList without optimize it, or change my code to create the triangleList in a right way, but I still don´t know how to do it.

 

Here is the code I use to create the triangleList:

static List<Vector3f> createTriangleListFromStrip(ByteBuffer stripBuffer){
		
		List<Vector3f> facesList= new ArrayList<Vector3f>();
		stripBuffer.clear();
		
		int a=0;
		int b=0;
		int c=0;
		int flagPar=0;
		
		for(int i=0;i<(stripBuffer.capacity()/2)-2;i++){
			
			if(i==0){
				a=Short.reverseBytes(stripBuffer.getShort());
				b=Short.reverseBytes(stripBuffer.getShort());
				c=Short.reverseBytes(stripBuffer.getShort());
				
				if(a!=b & a!=c & b!=c ){
					Vector3f face= new Vector3f();
					face.setX(a);
					face.setY(b);
					face.setZ(c);
					facesList.add(face);
				}
			}
			else{
				a=b;
				b=c;
				c=Short.reverseBytes(stripBuffer.getShort());
				
				if(a!=b & a!=c & b!=c ){
					Vector3f face= new Vector3f();
					face.setX(a);
					face.setY(b);
					face.setZ(c);
					facesList.add(face);
				}
			}
		}
		return facesList;
	}


#17 DiegoSLTS   Members   -  Reputation: 1930

Like
0Likes
Like

Posted 19 April 2014 - 08:43 AM

Try something like this:

static List<Vector3f> createTriangleListFromStrip(ByteBuffer stripBuffer){
		
		List<Vector3f> facesList= new ArrayList<Vector3f>();
		stripBuffer.clear();
		//Maybe you need to check if stripBuffer has enough elements, I'm not sure if you test that before calling this method
		int a=Short.reverseBytes(stripBuffer.getShort());
		int b=0;
		int c=Short.reverseBytes(stripBuffer.getShort());
		
		for(int i=2;i<(stripBuffer.capacity()/2)-2;i++){ //check if the "-2" still makes sense, I'm not sure what capacity() returns (byte size maybe?)
			
			if(i%2==0) {
				b = c;
			} else {
				a = c;
			}
			
			c = Short.reverseBytes(stripBuffer.getShort());
			
			if(a!=b & a!=c & b!=c ){
				Vector3f face= new Vector3f();
				face.setX(a);
				face.setY(b);
				face.setZ(c);
				facesList.add(face);
			}
		}
		return facesList;
	}

Also, doesn't Vector3f have a constructor with parameters? If you can do something like

Vector3f face= new Vector3f(a,b,c);
faceList.add(face);

the code is much readable.


Edited by DiegoSLTS, 19 April 2014 - 08:47 AM.


#18 jor1980   Members   -  Reputation: 151

Like
0Likes
Like

Posted 19 April 2014 - 01:07 PM

Your code works ok, i made my own but was wrong, Now  the mesh I think looks almost correct maybe there is one triangle with the normal inverted at the bottom or with  another error, but this the result now:

ao71.jpg

 

I would like that this mesh looks like in blender, but i don´t know if my problem is the material or the lights.

This is the blender look:

uylo.jpg


Edited by jor1980, 19 April 2014 - 02:03 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS