Wierd flip normals problem with polygonal modelling

The interface, modeling, 3d editing tools, import/export, feature requests, etc

Moderators: jesterKing, stiv

Post Reply
Brij
Posts: 25
Joined: Tue Dec 17, 2002 1:19 am
Contact:

Wierd flip normals problem with polygonal modelling

Post by Brij »

Hey there.

First of all, I must say the new 2.28 is very cool.
I just love that new interface, and the new shading in the mesh editing modes.

Anyways. Here is my problem. I'm creating a human head, but whenever I join to meshes, I get all sorts of problems with the mesh. Mainly the normals get all flipped, so parts of the face(quad's and tri's) look black..so then I have to go in manually and flip everything(in face select mode).

My Question is why is blender doing this?...(its always done this)...and how can I fix it?

-Brij

nyoxi
Posts: 0
Joined: Fri Jul 25, 2003 8:14 pm

Post by nyoxi »

If you turn on Double-sided in Mesh options you need not to care about normals in most cases.

And there is "Flip normals outside" command. But it works good only with convex meshes

ZGravity
Posts: 0
Joined: Wed Aug 06, 2003 9:39 pm

Post by ZGravity »

Hmm, I hardly ever use double sided myself, just 'cause you can end up with normals that look... uhh, normal, but give you weird deformations when you try to apply a subsurf, or even just smooth rendering.

Anyway, there's always Ctrl+N to recalculate normals automatically, but that's only really useful if you have a fairly simple mesh. Sounds like you run into problems when you join a mesh that's been flipped, one of the symmetrical sides always gets its normals reversed in the process of joining. Best thing to do, I think is just before joining the meshes, unflip the objects, and then flip it back to the direction you want while in edit mode. That way the two meshes both have the same scaling when you join, instead of one being reversed.
Dunno if there's a better way, but it works ok for me.

Brij
Posts: 25
Joined: Tue Dec 17, 2002 1:19 am
Contact:

Post by Brij »

Thanks guys.

Zgravity.
I used your method, fliping the verts, then joining, then when I join, everything is back to good.

Seems to work so far.

Thanks for the quick responses.

-Brij

cydergoth
Posts: 0
Joined: Wed Sep 10, 2003 8:58 pm
Location: Bristol, UK

Validation of polygon winding (normal direction)

Post by cydergoth »

Here is some code which verifies the polygon winding in an mesh. Sorry, it is in C#, but should be easy enough to understand. Maybe you could use something like this to fix this problem? [Its probably sub-optimal]

Note: It assumes a vertex list which is indexed by a list of indices where each polygon is consecutive in the list of indices and the number of vertices in each polygon is defined in the polygon array.

private void computeSharedVertexMap() {
sharedVertexMap=new int[VertexCount][]; // Array of vertices tagged with polygons which share them

// For each polygon, tag each vertex which is part of that polygon with the polygon number
int index=0;
for(int I=0;I<polygons.Length;I++) {
for(int J=0;J<polygons;J++) {
int vertex=indices[index++];
if(sharedVertexMap[vertex]==null) {
// No existing entry for this vertex
sharedVertexMap[vertex]=new int[1];
sharedVertexMap[vertex][0]=I;
}
else {
// Entry exists so check to see if this polygon is already registered and add it if not
int[] shared=sharedVertexMap[vertex];
int K;
for(K=0;K<shared.Length;K++) {
if(shared[K]==I) break;
}
if(K==shared.Length) {
// Expand entry array and add polygon to array
int[] temp=shared;
shared=new int[temp.Length+1];
Array.Copy(temp,shared,temp.Length);
shared[temp.Length]=I;
sharedVertexMap[vertex]=shared;
}
}
}
}

// Dump the vertex map if tracing
Trace.WriteLineIf(traceVertexMapping.TraceVerbose,"Vertex map");
for(int I=0;I<sharedVertexMap.Length;I++) {
if(sharedVertexMap==null) throw new Exception("Empty shared vertex map @"+I);
if(sharedVertexMap.Length<3) throw new Exception("Too few vertices @"+I+" "+sharedVertexMap.Length+" "+vertices);
if(traceVertexMapping.TraceVerbose) {
for(int J=0;J<sharedVertexMap.Length;J++) {
Trace.Write(sharedVertexMap[J]+" ");
}
Trace.WriteLine("");
}
}
}


/// <summary>Cache of the polygon adjacency information<summary>
[NonSerialized]
private int[][]adjacent;

/// <summary>Fetch the map of adjacent polygons</summary>
/// <remarks>The map contains one entry per polygon in the same order as the polygon list, each entry is an array
/// containing the ordinals of all adjacent polygons</remarks>
public int[][] AdjacencyMap {
get {
if(adjacent==null) computeAdjacency();
return adjacent;
}
}

private void computeAdjacency() {
adjacent=new int[FaceCount][];
if(sharedVertexMap==null) computeSharedVertexMap();

int index=0;

// A polygon is adjacent to another one if it shares two vertices in common
for(int I=0;I<polygons.Length;I++) {
for(int J=0;J<polygons;J++) {
int[] shared=sharedVertexMap[indices[index+J]];
for(int K=0;K<shared.Length;K++) {
int adjacentPolygon=shared[K];
if(adjacentPolygon==I) continue;
// Possible candidate. Check the other vertices to see if there is a second one in common
for(int L=0;L<polygons;L++) {
if(L==J) continue;
int[] shared2=sharedVertexMap[indices[index+L]];
for(int M=0;M<shared2.Length;M++) {
if(shared2[M]==adjacentPolygon) {
if(adjacent==null) {
adjacent[I]=new int[1];
adjacent[I][0]=adjacentPolygon;
}
else {
int N;
for(N=0;N<adjacent[I].Length;N++) {
if(adjacent[I][N]==adjacentPolygon) break;
}
if(N==adjacent[I].Length) {
int[] temp=adjacent[I];
adjacent[I]=new int[temp.Length+1];
Array.Copy(temp,adjacent[I],temp.Length);
adjacent[I][temp.Length]=adjacentPolygon;
}
}
}
}
}
}
}
index+=polygons[I];
}


Trace.WriteLineIf(traceAdjacencyMapping.TraceVerbose,"Adjacency map");
for(int I=0;I<adjacent.Length;I++) {
if(adjacent[I]==null) throw new Exception("No adjacent polygons @"+I);
if(adjacent[I].Length!=polygons[I]) throw new Exception("Adjacent polygon count "+adjacent[I].Length+" doesn't match polygon edge count "+polygons[I]);

if(traceAdjacencyMapping.TraceVerbose) {
for(int J=0;J<adjacent[I].Length;J++) {
Trace.Write(adjacent[I][J]+" ");
}
Trace.WriteLine("");
}
}
}

/// <summary>Verify the winding rule for each polygon in this geometry object to check that it is consistent</summary>
/// <remarks>The first polygon is assumed to be wound correctly.</remarks>
public int[] VerifyWinding() {
if(adjacent==null) computeAdjacency();
ArrayList rejects=new ArrayList();
// Now cycle through the polygons and verify that each polygon has the reverse edge direction to the
// adjacent polygon along the common edge
bool[] scanned=new bool[adjacent.Length];
Stack pending=new Stack(adjacent.Length);
pending.Push(0);
while(pending.Count>0) {
int current=(int)pending.Pop();
scanned[current]=true;
int[] otherPolygons=adjacent[current];
for(int I=0;I<otherPolygons.Length;I++) {
if(!scanned[otherPolygons[I]] && !checkWinding(current,otherPolygons[I])) {
rejects.Add(otherPolygons[I]);
scanned[otherPolygons[I]]=true;
}
else if(!scanned[otherPolygons[I]]) pending.Push(otherPolygons[I]); // Don't push known bad polygons
}
}

return (int[])rejects.ToArray(typeof(int));
}

private bool checkWinding(int current,int other) {
if(current==other) throw new Exception("Check winding called with same polygon twice!");
// Find common pair of vertex indices between polygons index current and other
int currentIndexBase=0;
int otherIndexBase=0;
for(int I=0;I<current;I++) {
currentIndexBase+=polygons[I];
}
for(int I=0;I<other;I++) {
otherIndexBase+=polygons[I];
}

for(int currentVertexIndex=0;currentVertexIndex<polygons[current];currentVertexIndex++) {
for(int otherVertexIndex=0;otherVertexIndex<polygons[other];otherVertexIndex++) {
if(indices[currentVertexIndex+currentIndexBase]==indices[otherVertexIndex+otherIndexBase]) {
Trace.WriteLine("Found common vertex between @"+current+" and @"+other+" as "+currentVertexIndex+" and "+otherVertexIndex);
bool valid=false;

int nextCurrentVertexIndex=(currentVertexIndex+1)%polygons[current];
int nextOtherVertexIndex=otherVertexIndex-1;
if(nextOtherVertexIndex<0) nextOtherVertexIndex=polygons[other]-1;
valid|=(indices[nextCurrentVertexIndex+currentIndexBase]==indices[nextOtherVertexIndex+otherIndexBase]);

nextCurrentVertexIndex=currentVertexIndex-1;
nextOtherVertexIndex=(otherVertexIndex+1)%polygons[other];
if(nextCurrentVertexIndex<0) nextCurrentVertexIndex=polygons[current]-1;
valid|=(indices[nextCurrentVertexIndex+currentIndexBase]==indices[nextOtherVertexIndex+otherIndexBase]);

return valid;
}

Post Reply