reversed UVs [SOLVED]

On 07/03/2017 at 12:51, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   R18 
Platform:   Windows  ;   
Language(s) :     C++  ;

I was wondering about detecting reversed UVs, and tried the following:

Using a UserArea I draw the UVs (color coded by island) and check the winding of the UVW edges. When winding is counterclockwise I draw a dot at the barycenter of the polygon.
Now I have this sphere which I unwrapped into 3 islands, and see that the blue one is reversed.

The white edges (candidate selections) in the viewport and the UV view clearly indicate that the island in the UV view is reversed. Also the textured version of the screenshot (see below) show that this blue island has reversed UVs.

But when I look at the dots, I only see some of the polygons of the blue island being detected as having a counterclockwise winding. I would have assumed that all polygons are reversed, since the textured screenshot clearly show all the texture parts assigned to the polygons of the blue island being reversed.
Strangly, also the non reversed (red) island does have a single UV polygon detected as reversed, while its texture is not reversed.

So, is this just a wrong assumption to try and use this winding to detect an UV polygon being reversed, or am I missing something?

I thus far have noticed that "reversed" UV islands have more than 50% of its UV polygons detected as having counterclockwise winding, while a non reversed island has less then 50% UV polygons detected as having counterclockwise winding. So, would this be the criteria to detect if an UV island is "reversed" ???

On 08/03/2017 at 09:58, xxxxxxxx wrote:

Hi C4DS, thanks for writing us.

Although I've carefully read your exhaustive description there's still one point to be clarified from my side: by  "winding" you mean the position of the UV origin and the direction of the U and V axis?
For example considering the a quad indexed by letters
A ---- B
|        |
|        |
C ---- D

where A(0,0), B(1,0), D(1,1), C(0,1) is CW and B(0,0), A(1,0), C(1,1), D(0,1) is CCW?

Thanks, Riccardo

On 08/03/2017 at 12:47, xxxxxxxx wrote:

Hey Riccardo,

With "winding" I mean the sequence of polygon vertices (or uv points). I thought this was the term used when finding out if a polygon defines a closed surface, or if it represents a hole. Alternatively, I guess, it could be used to find out if it's normal is pointing upwards/outwards or downwards/inwards.

Nice, you're using ASCII art, didn't think of using that. Might have been easier to explain than my exhaustive description (sorry about that).

Assume a polygon with vertices a, b, c, d. Clockwise winding would then be

a ---- b

|        |
|        |
d ---- c

and CCW

a ---- d

|        |
|        |
b ---- c

Or, indeed as you wrote:

b ---- a

|        |
|        |
c ---- d

For the UV points of an UV polygon I would expect the same, the sequence of the UV points could then be seen as having a winding as well.
I don't quite follow why you refer to the U and V axis?
I mean, OK, the U axis goes from left to right (0 to 1) and the V axis from top to bottom (0 to 1).

But the winding of the UV polygon (so I thought) is simply the order of its UV points in the UVW struct:

uvw.a ---- uvw.b

|        |
        |        |
uvw.d ---- uvw.c

The above would represent a clockwise UV polygon, and would face upwards (texture not flipped)
The next one:

uvw.a ---- uvw.d

|        |
        |        |
uvw.b ---- uvw.c

would represent a counterclockwise UV polygon and would be reversed (texture mirrored).

On 09/03/2017 at 09:59, xxxxxxxx wrote:

Hi C4DS, thanks for the additional explanation and for confirming we were on the same page (although my first post could look a bit misleading).
From my tests I'm able to properly detect (and mark) all those polygon whose UV are CCW respect those which are CW. Can you share your test scene? How are you checking the UV winding? I actually evaluate the virtual area of the UV and test for being negative (CW) or positive (CCW).

C++ snippet

// retrieve the UVW tag for the object  
UVWTag* uvw = (UVWTag* )obj->GetTag(Tuvw);  
if (!uvw)  
  return false;
// retrieve the number of UV stored  
Int32 uvwCnt = uvw->GetDataCount();
//cycle through the number of UVs and evaluate the winding   
for (Int32 i = 0; i < uvwCnt; ++i)  
  Float64 area = 0;  
  UVWStruct uvwData = uvw->GetSlow(i);  
  area += (uvwData.a.x + uvwData.b.x)*(uvwData.a.y - uvwData.b.y);  
  area += (uvwData.b.x + uvwData.c.x)*(uvwData.b.y - uvwData.c.y);
    // distinguish the quads from the tris  
  if (uvwData.c == uvwData.d)  
      area += (uvwData.c.x + uvwData.a.x)*(uvwData.c.y - uvwData.a.y);  
      area += (uvwData.c.x + uvwData.d.x)*(uvwData.c.y - uvwData.d.y);  
      area += (uvwData.d.x + uvwData.a.x)*(uvwData.d.y - uvwData.a.y);  
    if (area > 0)  
      GePrint("UV set for polygon (" + String::IntToString(i) + ") is CCW");  
      GePrint("UV set for polygon (" + String::IntToString(i) + ") is CW");  

Best, Riccardo

On 09/03/2017 at 10:44, xxxxxxxx wrote:

Hey Riccardo,

Mmm ... feeling embarassed now.

I used following formula to evaluate the winding
// Sum over the edges (x2 - x1)*(y2 + y1)

  Float64 sum = 0.0;  
  sum += (uvw.b.x - uvw.a.x) * (uvw.b.y + uvw.a.x);  
  sum += (uvw.c.x - uvw.b.x) * (uvw.c.y + uvw.b.x);  
  if (uvw.c == uvw.d) // triangle  
      sum += (uvw.a.x - uvw.c.x) * (uvw.a.y + uvw.c.x);  
      sum += (uvw.d.x - uvw.c.x) * (uvw.d.y + uvw.c.x);  
      sum += (uvw.a.x - uvw.d.x) * (uvw.a.y + uvw.d.x);  
  //return sum < 0; // negative if counterclockwise -> reversed  
  return sum >= 0; // positive if counterclockwise -> reversed  

But I can confirm that your code DOES provide the correct detection.
So the formula actually should have been (x1 + x2)*(y1 - y2). Silly, silly me.


On 09/03/2017 at 21:59, xxxxxxxx wrote:

Well CD4S, glad mine was working but if you properly check yours you see that in your formula where y1 was expected you were using x1. Yours is correct as well, in the end it just applied to an affine linear space, and the culprit is just a wrong cut and paste.


On 09/03/2017 at 22:29, xxxxxxxx wrote:

Oh my!
My sincere apologies for having started this thread.

You are right Riccardo, my implementation was just one big copy-paste typo.
Should have checked that in more details before asking questions, and wasting everyone's time.

So, in the end, the idea was good. The execution on the other hand ... meuh.

Sorry, sorry, sorry.
Lesson learned.