Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Certain models have incorrect normals #34

Open
BagelOrb opened this issue Apr 30, 2015 · 17 comments
Open

Certain models have incorrect normals #34

BagelOrb opened this issue Apr 30, 2015 · 17 comments
Labels
Type: Bug The code does not produce the intended behavior.

Comments

@BagelOrb
Copy link
Contributor

Some models show wrong overhangs.

Perhaps the normals as described in the STL file are incorrect.

It would then be nice to recalculate the normals so that the right pieces are colored red.
pluggcura_wrong_overhangs

@BagelOrb BagelOrb added Type: Bug The code does not produce the intended behavior. new feature labels Apr 30, 2015
@daid
Copy link
Contributor

daid commented May 1, 2015

I think this can be fixed easier then normal fixing, but just changing the shader to assume that the normal could be flipped as well.

@BagelOrb
Copy link
Contributor Author

BagelOrb commented May 1, 2015

So then we should autodetect whether the normals are inverted?

@nallath
Copy link
Member

nallath commented May 1, 2015

No, we just use a shader that uses both options. Recalculating normals is kinda tricky to get right (especially with broken models)

@Ghostkeeper
Copy link
Collaborator

Computing normals is fairly easy if the order of the vertices is correct, i.e. using the right-hand rule (see https://en.wikipedia.org/wiki/Normal_%28geometry%29#Uniqueness_of_the_normal). If that is the case, there are a few edge cases, such as triangles without surface area (2 or more vertices have the same coordinates), but no big hurdles.

If the vertices have the wrong order that is very hard to detect, since it involves detecting which side of the triangle is on the inside, which is expensive to compute and undefined if the model is not watertight.

@daid
Copy link
Contributor

daid commented May 1, 2015

@Ghostkeeper That's how the normals are calculated right now. The problem is some applications just don't keep the winding order properly at all (as with the above model)

OpenSCAD also has a issue where if you make a hollow cube, the inner cube will have the winding order the wrong way around.

@BagelOrb
Copy link
Contributor Author

BagelOrb commented May 3, 2015

Just find the highest triangle and check whether the normal according to the assumed winding order also has a positive Z component, if not, flip all normals.

Highest triangle =
from all triangles sharing the highest vertex, the one with the normal with the highest absolute z component.

@awhiemstra
Copy link
Contributor

So let's split this into two issues - there is a set of models for which the normals are inverted which creates the wrong overhang display. Then there is the feature request of adding a "recalculate normals" option somewhere in the UI. The first thing is a bug that needs to be solved at one point or another. The other is a feature with dubious usefulness that will not be implemented before the 15.06 final release in any case. Please make a new feature request for that in the Cura repository. Any further discussion in this issue should be about fixing the normals bug.

@awhiemstra awhiemstra changed the title Feature Request: recalculate normals Certain models have incorrect normals May 4, 2015
@daid
Copy link
Contributor

daid commented May 4, 2015

FYI: Models with just all the normals 100% inverted are rare. Models with some of the normals correct, but some wrong are more common.

@awhiemstra
Copy link
Contributor

Yes, it is not an easy thing to solve and I do not assume we will be able to solve it before release.

@BagelOrb
Copy link
Contributor Author

BagelOrb commented May 4, 2015

There are two kinds of bugs - consistently incorrect winding order or inconsistenly incorrect winding order. Nobody mentioned adding an option in the UI!

As @daid said, the first one is not that common, so it might be an idea to solve for incorrect winding order for all faces... This is no high priority though.

@Ghostkeeper
Copy link
Collaborator

I realise that this issue won't be fixed soon due to time constraints, but I thought of a solution that's easy to implement and (I think) quite fast to compute. Here follows an explanation.

You can find inconsistencies by looking at adjacent triangles. The normal vectors of two adjacent triangles must be on the same side of the mesh. Please see this example:
normalvectorcorrection

Here we have a geometry consisting of four vertices V1, V2, V3 and V4, and given are two normals N1 and N2, of which we know N1 is correct but would like to check N2. What we'll do is compute whether V4 lies above the plane of the other triangle (comprised of V1, V2 and V3) or below. If V4 lies above the plane (in the direction of N1), the surface is concave and the normals must point towards each other. If V4 lies below the plane (in the opposite direction of N1), the surface is convex and the normals must point away from each other.
In the drawing above, V4 is meant to be below the plane through V1, V2 and V3, facing towards N1 (though the image is admittedly a bit ambiguous on its depth). The surface is therefore convex, and N2 is wrong.

To compute whether V4 lies above or below the plane, project it onto the normal perpendicular to the plane. Conveniently, this is already known as N1:
Proj1 = (V4 - V1) · N1
If Proj1 > 0, the two triangles are concave. If Proj1 < 0, the two triangles are convex. If Proj1 = 0, they are coplanar. In place of V1 you could use any point on the plane (so V1, V2 or V3).

The second step is to find whether N2 is pointing towards the other triangle or away from it. To do this, project N2 onto either the line V1-V2 or the line V1-V3. It doesn't matter which. Like so:
Proj2 = N2 · (V1 - V2)
This gets us the component of N2 that points towards V1. If Proj2 > 0, N2 points towards V1. If Proj2 < 0, N2 points away from V1. If Proj2 = 0, it is perpendicular from the viewpoint of V2.

This all can be summarised in the following code:

//(V4 - V1).dot(N1) finds whether V4 is above the plane through the triangle of N1 (positive) or below (negative).
//N2.dot(V1 - V2) finds whether N2 points towards V1 (positive) or away from it (negative).
//If V4 is above the triangle, N2 must point towards V1. Otherwise, it must point away.
//In other words, if proj_triangle_side is positive, proj_normal_dir must be positive. If proj_triangle_side is negative, proj_normal_dir must be negative.
//Therefore, proj_triangle_side * proj_normal_dir must be positive! If it's not, invert N2.
float proj_triangle_side = (V4 - V1).dot(N1);
float proj_normal_dir = N2.dot(V1 - V2);
float projection_product = proj_triangle_side * proj_normal_dir;
if(projection_product < 0) //Either concave and pointing away from each other, or convex and towards each other.
{
    N2 *= -1; //Invert the faulty normal.
}
else if(projection_product == 0) //Coplanar surfaces.
{
    N2 = N1; //Normals are the same then anyway, so take the one that's sure to be correct.
}

To correct all normals, you could find an initial correct normal using @BagelOrb's suggestion, finding the highest triangle and asserting that normal to have a positive Z component. Then you can propagate the correct normal throughout the mesh, for instance with breadth-first search. This would have to be repeated for every separate component, since it must propagate via two connected vertices between the triangles (one is not enough, since the concavity or convexity could be different then!).

I heard though that the implementation is currently such that all triangles are disconnected from each other (meaning that in the above example, V2 and V3 are double). This makes it hard to find two adjacent triangles at all and defeats this algorithm's purpose if that is the case... Anyway, this is just for future reference if anyone would eventually like to implement some more model correction.

@awhiemstra awhiemstra added this to the Not in release yet milestone May 8, 2015
@BagelOrb
Copy link
Contributor Author

The direction of a face is implicit in the winding order of its vertices. The normal is pointing upward from the plane in which the vertices are ordered counter-clockwise. As such we can just see whether two faces are flipped the same way by checking the order on the vertices; when two connected faces have their edges in the opposite direction they are correctly flipped.
uitlegfacedirection

@Ghostkeeper your algorithm is superfluous.

@Ghostkeeper
Copy link
Collaborator

@BagelOrb Yeah that seems like a much more elegant solution than my overthinking, if the normals are indeed implicit in the winding order. It probably works better with edge cases as well.

@daid
Copy link
Contributor

daid commented May 19, 2015

difference() { cube(20, center=true); cube(10, center=true); }

OpenSCAD code.

Result is a cube with a hollow center. But the center has an improper winding order.

Good luck ;-)

@BagelOrb
Copy link
Contributor Author

The center hole cannot be seen, so it's not problem.

@awhiemstra
Copy link
Contributor

Referenced the wrong commit, this shouldn't be closed yet.

@awhiemstra awhiemstra reopened this Jun 24, 2015
@fieldOfView
Copy link
Contributor

MeshTools plugin has a fairly robust option to fix normals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug The code does not produce the intended behavior.
Projects
None yet
Development

No branches or pull requests

6 participants