Anim8or Community

Please login or register.

Login with username, password and session length
Advanced search  

News:

An update to Anim8or, v1.00b, is available with a few bug fixes. Get your copy HERE. See the "ReadMe" file for details.

Author Topic: Bug/Issue: Normals (ASL)  (Read 1517 times)

Raxx

  • Administrator
  • Hero Member
  • *****
  • Posts: 1447
    • View Profile
Bug/Issue: Normals (ASL)
« on: March 07, 2016, 02:15:49 am »

I'm posting here since this seems like a bug to me. I'm working on a new set of scripts and part of it detects the sharpness/creases of edges in the geometry. I'm doing this by measuring the dot product of the normals of the two faces connected to an edge to get its sharpness (1 is flat, 0 is not).

To get the face normals, I'm calculating the mean of all the normals of the points in a face (ie (normal1 + normal2 + normal3)/3). I'm getting these normals using GetNormal(GetFaceNormalIndex()), rather than calculating the geometric normal manually. I'd rather not go through the hassle of triangulating and math-ing and combining for each face since it's more work, and more convoluted since I have to work with mesh data not tridata.

The issue is that I have a symmetrical mesh, and at a lot of locations a face on one side comes up with different normals than the corresponding face on the other side. I get why this can happen for n+3 sided faces, but it's also happening for triangles! And yet when I show normals in the Options->Debug menu, the triangles' normals look exactly symmetrical when the ASL version is not. This leads me to believe that there is some sort of lack of precision going on or something. My script needs precision!

So here I am reporting it. I hope it can be fixed if it's not an error on my part. Below and attached are examples that I hacked together to show what I mean.

ASL Snippet
  1. file $c;
  2. shape $s, $childShapes[0];
  3. int $i, $j, $k;
  4. float $numSides;
  5. point3 $faceNormal;
  6.  
  7. $c.open("$console", "w");
  8.  
  9. project.curObject.GetShapes($childShapes);
  10.  
  11. for $i = 0 to $childShapes.size - 1 do
  12. {
  13. if($childShapes[$i].GetKind() == SHAPE_KIND_MESH)
  14. {
  15. $s = $childShapes[$i];
  16.  
  17. for $j = 0 to $s.GetNumFaces() - 1 do
  18. {
  19. if ($s.GetFaceSelected($j) == 1)
  20. {
  21. $faceNormal = $s.GetNormal($s.GetFaceNormalIndex($j, 0));
  22. $numSides = $s.GetNumSides($j);
  23.  
  24. for $k = 1 to $numSides - 1 do
  25. {
  26. $faceNormal = $faceNormal + $s.GetNormal($s.GetFaceNormalIndex($j, $k));
  27. }
  28.  
  29. $faceNormal = normalize(($faceNormal.x/$numSides, $faceNormal.y/$numSides, $faceNormal.z/$numSides));
  30.  
  31. $c.print("\nFace %d has normals (%.3f, %.3f, %.3f)\n", $j, $faceNormal);
  32. }
  33. }
  34. }
  35. }
  36.  
  37. $c.close();
  38.  

The above script lets you select faces, so that when you run it it'll print in the console what the face normal is for each of the faces.
Logged

NickE

  • Full Member
  • ***
  • Posts: 157
    • View Profile
Re: Bug/Issue: Normals (ASL)
« Reply #1 on: March 09, 2016, 01:08:06 am »

Raxx,

When I calculate the face normals from the average of the face vertex normals (calculated from the edge vectors from each point of the face), I do get different values than your script returns.  The edge vector calculated values return symmetrical normals for mirrored faces on your attached mesh.  I modified your script to add the manually calculated normals to the output.  You can see the variation in the individual vertex based face normals.  It seems the algorithm that Anim8or is using is significantly different or is suffering from a precision problem.

Code: [Select]
file $c;
shape $s, $childShapes[0];
int $i, $j, $k;
int $numSides;
point3 $faceNormal;

//added
int $l,$back,$fwd;
point3 $facePoint[0];
point3 $facePointNormal[0];
point3 $fPNavg;
 
$c.open("$console", "w");
 
project.curObject.GetShapes($childShapes);
 
for $i = 0 to $childShapes.size - 1 do
{
if($childShapes[$i].GetKind() == SHAPE_KIND_MESH)
{
$s = $childShapes[$i];
 
for $j = 0 to $s.GetNumFaces() - 1 do
{
if ($s.GetFaceSelected($j) == 1)
{
//$faceNormal = $s.GetNormal($s.GetFaceNormalIndex($j, 0));
$faceNormal=(0.0,0.0,0.0);
$fPNavg=(0.0,0.0,0.0);
$numSides = $s.GetNumSides($j);
        $facePoint.size = $numSides;
        $facePointNormal.size = $numSides;
for $k = 0 to $numSides - 1 do
{
$faceNormal = $faceNormal + $s.GetNormal($s.GetFaceNormalIndex($j, $k));
$facePoint[$k]=$s.GetPoint($s.GetFacePointIndex($j,$k));
$facePointNormal[$k]=(0.0,0.0,0.0);
}
 
$faceNormal = normalize(($faceNormal.x/(1.0 * $numSides), $faceNormal.y/(1.0 * $numSides), $faceNormal.z/(1.0 * $numSides)));

for $k = 0 to $numSides-1 do
{
          $back = $k - 1;
          if ($back < 0) $back = $numSides - 1;
          $fwd = $k + 1;
          if ($fwd > ($numSides - 1)) $fwd = 0;
          $facePointNormal[$k] = normalize(cross(($facePoint[$back]-$facePoint[$k]),($facePoint[$fwd]-$facePoint[$k])));
          $c.print("Calc FP %d Normal (%.3f,%.3f,%.3f)\n",$k,$facePointNormal[$k]);
          $fPNavg = $fPNavg + $facePointNormal[$k];
        }
        $fPNavg = normalize(($fPNavg.x / (1.0 * $numSides),$fPNavg.y / (1.0 * $numSides),$fPNavg.z / (1.0 * $numSides)));
$c.print("Face %d Anim8or normals (%.3f, %.3f, %.3f) Calc (%.3f, %.3f, %.3f)\n", $j, $faceNormal, $fPNavg);
}
}
}
}
 
$c.close();
 

NickE
Logged

Steve

  • Administrator
  • Hero Member
  • *****
  • Posts: 1532
    • View Profile
Re: Bug/Issue: Normals (ASL)
« Reply #2 on: March 09, 2016, 02:55:26 am »

My first comment is that averaging 3D vectors is an ill defined operation. It really doesn't work 100% as you might expect.  But it can be useful.  My second comment is that generating smooth geometric normals for a polygonal mesh is also problematic. There isn't really a good definition of how that should be done.

For an extreme example, average and normalize the three normals (1,0,0), (-1,0,0) and (0,1,0) and you get (0,1,0), which is probably not what you'd want.

That said, if you want to make a picture in which a polygonal mesh appears to be smooth, a good (i.e. fast) way to do this is to approximate the normal at the corners of each polygon and interpolate those as you do colors and texture coordinates. That's how the majority of 3D graphics today works.

Anim8or computes vertex normals like this:

1) Compute a geometric normal for the polygon by computing all of the individual corner's normals.
2) Pick the one with the angle closest to 90 degrees of all corners.  This is the "Face Normal" that you are accessing in the script.
3) Compute "Smooth Groups" for all faces in a mesh.  These are faces that are adjoined by solid angles less than or equal to the "Smooth Angle" of the mesh.  This is a fairly complex computation because you first need to compute the connectedness of the faces and then propagate smooth group id's across it.
4) For each vertex of each polygon, average all the normals of any faces common to that vertex that are in the same smooth group. (Yes, I know I said that the average isn't well defined, but it's the best fast way to do this that I know of :) )

A better way to do step #4 might be to weight each normal by the magnitude of the angle of it's corner of the polygon. That way narrow corners won't get as much weight as wider ones which could eliminate some of the artifacts. Someday I'll get time to look into this :)

Raxx: GetFaceNormal returns the result of step 4.  So you are averaging the vertex normals which does not yield the value of the face normals.  Unfortunately, at this time ASL does not have a function to return the face normal but I'll add one.

« Last Edit: March 09, 2016, 03:44:29 am by Steve »
Logged

Raxx

  • Administrator
  • Hero Member
  • *****
  • Posts: 1447
    • View Profile
Re: Bug/Issue: Normals (ASL)
« Reply #3 on: March 11, 2016, 10:48:11 am »

Nicke, thanks for taking the time to show that the manual way produces more consistent results. I tried yours and a few other surface normal algorithms and got mostly the same consistency.

Steve, thanks for the detailed explanation as to why Anim8or's results won't necessarily be consistent.

For now, I'll just go with Newell's method of calculating surface normals, which provides good enough results (takes only ~1 second to calculate ~197,000 faces). If a system time method gets put in, I'll pick whichever I determine to be the fastest method. :)
Logged

Steve

  • Administrator
  • Hero Member
  • *****
  • Posts: 1532
    • View Profile
Re: Bug/Issue: Normals (ASL)
« Reply #4 on: March 11, 2016, 10:58:52 pm »

Raxx: Yes, and I don't think any algorithm can be consistent for faces that aren't perfectly flat.  There will be at least one vertex that doesn't agree locally with any single normal. You just have to choose a method and use it.
Logged