Go Back   AC3D Forums > Technical > AC3D Developers
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Display Modes
Old 29th August 2005, 12:23 PM   #1
Gary E Sanders
Junior Member
Member
 
Join Date: Nov 2003
Posts: 12
Default How are AC3D Normal Calculations done?

I'm using ac3d files directly in an OpenGL program. In doing the normal calculations I notice a couple of things.

1) My method is significantly slower than AC3D
2) I smooth over adjacent surfaces that AC3D doesn't.

Basically for each surface set to smooth I check for a shared vertex with any other surface in that object, then calculate the average of the surface normals of all smooth surfaces joining at that vertex to get the vertex normal.

An illustration of the problem is a box drawn in ac3d and set to smooth surfaces doesn't look any different than the same box with the surfaces set to flat when viewed in AC3D. Using the normal calculation methodology above it looks like a poor sphere.

What I would like to do is duplicate the surface normal calculation done in ac3d ( I know I can export normals, but I want to use the .ac file directly) Perhaps someone, (Andy?) can give a hint on determining which surfaces need to be included in the vertex normal calculation.

thanks
ges
Gary E Sanders is offline   Reply With Quote
Old 29th August 2005, 01:47 PM   #2
Dennis
Senior Member
Professional user
 
Dennis's Avatar
 
Join Date: Jul 2003
Posts: 899
Default

I believe AC3D does vertex smooth using all shared surfaces of a vertex.

You won't see the smoothing on a cube, however, because of the crease angle - you'll have to open the cube's object properties and push the Crease angle above 90 to see vertex smoothing in action.

Sounds like your base calculation is fine, you'll just have to account for the surface angle. Also, you won't be able to get away with a single vertex normal where you have mixed smooth/flat angles due to crease (i.e., the vertices at the edge of a cylinder where the top is flat-shaded and the sides are smooth).

Good luck
Dennis
Dennis is offline   Reply With Quote
Old 29th August 2005, 03:06 PM   #3
Gary E Sanders
Junior Member
Member
 
Join Date: Nov 2003
Posts: 12
Default

I guess I was unfamiliar with the crease angle. I took your advice on the cube properties and that is indeed what I see in my OpenGL code.
Let's see if I understand the concept. If the angle between an adjacent surface and the surface for which I am calculating normals is greater than the crease angle I ignore that surface when calculating the per vertex normal.
That makes sense, but, this adds another calculation in the loop for normals. I suppose I have a code optimisation problem, its at least an order of magnitude slower than AC3D for an object with 20000 verticies. I was certainly hoping there was a magic pill that would solve my problem.

thanks
ges
Gary E Sanders is offline   Reply With Quote
Old 29th August 2005, 03:32 PM   #4
Andy
Administrator
Professional user
 
Andy's Avatar
 
Join Date: Jun 2003
Posts: 4,563
Default

You have the right concept.

Here's AC3D's internal code that calculates the normals.

Code:
	
	// get the crease angle in radians
	float rad_angle = cos(ob->crease_angle * M_PI / 180.0f);

...

		{
			// vertex index will hold a list of each surface that references the vertex
			// first, clear the lists
			for (List *p = ob->pointlist; p != NULL; p = p->next)
				{
				Vertex *v = (Vertex *)p->data;
				vertex_set_index(v, NULL);
				}

			// for each sv, add the surface to the list of surfaces in each vertex
			for (List *spp = ob->surfacelist; spp != NULL; spp = spp->next)
				{
				Surface *s = (Surface *)spp->data;
				
				for (List *svp = s->vertlist; svp != NULL; svp = svp->next)
					{
					SVertex *sv = (SVertex *)svp->data;

					list_add_item_head((List **)&(sv->v->index), s);
					}
				}


			for (List *sp = ob->surfacelist; sp != NULL; sp = sp->next)
				{
				Surface *s = (Surface *)sp->data;

				for (List *svp = s->vertlist; svp != NULL; svp = svp->next)
					{
					SVertex *sv = (SVertex *)svp->data;

					sv->normal = s->normal;

					if ((vertex_get_index(sv->v) == 0))
						{
						continue; // next please
						}

					// so, surface is shaded
					// fl is list of faces that refer to this vertex
					List *fl = (List *)vertex_get_index(sv->v);

					for (List *flp = fl; flp != NULL; flp = flp->next)
						{
						Surface *os = (Surface *)flp->data;

						// ignore if the same surface
						if (s != os)
							{	
							// even if the surface is flat, we still calculate the normals
							// the shading may change and this saves recalulating normals again

							// calc dot product of this surface plus attached
							float dot = (DOTPRODUCT(&s->normal, &os->normal));

							if (dot > rad_angle)
								{
								ADDPOINTS(&os->normal, &sv->normal);
								}
							}
						}

					normalize_point(&sv->normal);
					}
				}


			for (List *vp = ob->pointlist; vp != NULL; vp = vp->next)
				{
				Vertex *v = (Vertex *)vp->data;

				list_free((List **)&vertex_get_index(v));
						
				}



			}
Notes:

1) that the vertex_index stuff is internal and you'll need to use another way of storing the vertex info.

2) The surface flag for shading is used when drawing the model - if a surface is 'smooth', the vertex normals are used. If a surface is 'flat', the surface-normal is used for each vertex.

3) the above code calculates the all normals in an object (not just those changed recently). Some bits of internal code have been removed but I've hopefully left the important stuff. If the crease angle is 0 or 180, you can optimize a bit.
Andy is offline   Reply With Quote
Old 29th August 2005, 04:50 PM   #5
Gary E Sanders
Junior Member
Member
 
Join Date: Nov 2003
Posts: 12
Default

Thanks Andy!

You are without a doubt the most responsive code developer I've ever dealt with. Thanks again.


ges
Gary E Sanders is offline   Reply With Quote
Old 30th August 2005, 10:14 AM   #6
Gary E Sanders
Junior Member
Member
 
Join Date: Nov 2003
Posts: 12
Default

Just a follow up.

Thanks Andy, the code snippit above did provide the speedup I was looking for. I implemented the prefiltering of surfaces referencing each vertex using the STL as a vector of lists which is populated as I read in the file. This step is so obvious in retrospect I'm ashamed I didn't think of it. Thanks again.
ges
Gary E Sanders is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT -4. The time now is 01:01 PM.


AC3D Forum
(C) Inivis Limited 2020