Find faces with mirrored UVs

Hello,

i try to find which faces has mirrored UV coords using python. Actual game engine do this in their shaders as well and the auto-unwrap functions of blender can do it too (obviously) since the Unrapping function creates propper aligned UVs. Reading UV-data of a face is easy:


import bpy


obj = bpy.context.active_object


for face in obj.data.polygons:
    for vert_idx, loop_idx in zip(face.vertices, face.loop_indices):
        uv_coords = obj.data.uv_layers.active.data[loop_idx].uv
        print("face idx: %i, vert idx: %i, uvs: %f, %f" % (face.index, vert_idx, uv_coords.x, uv_coords.y))

But how can i determine which UVs are mirrored? Must somehow be associated with the faces (or vertices) position in space. Maybe someone of you has a hint for me… Thank you guys.

usually you can do this by checking if the loop elements ordered clockwise or anti clockwise

What I usually do:
• loop through faces
• for each face loop through its first 3 UVs (known as loops). Don’t bother looping through them all as the first 3 are enough
• collect values using ‘Sum over the edges’ and compare at the end

here is an untested snippet


# Create BMesh from active object (e.g. selected one)
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uvLayer = bm.loops.layers.uv.verify()


for face in bm.faces:
    sum_edges = 0
    # Only loop 3 verts ignore others: faster!
    for i in range(3):
        uv_A = face.loops[i]
        uv_B = face.loops[(i+1)%3]
        sum_edges+= (uv_B.x - uv_A.x) * (uv_B.y + uv_A.y)


    # If sum negative its counter clockwise
    if sum_edges < 0:
        print("Face {} is counter clockwise".format(face.index))
    else:
        print("Face {} is clockwise".format(face.index))

Hoffentlich hilfts

Nice thank you so far :slight_smile:

That code is running (you missed to call the “co” property on uv_B and uv_A):


import bpy
import bmesh


# Create BMesh from active object (e.g. selected one)
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uvLayer = bm.loops.layers.uv.verify()


for face in bm.faces:
    sum_edges = 0
    # Only loop 3 verts ignore others: faster!
    for i in range(3):
        uv_A = face.loops[i].vert
        uv_B = face.loops[(i+1)%3].vert
        sum_edges += (uv_B.co.x - uv_A.co.x) * (uv_B.co.y + uv_A.co.y)
        
    if sum_edges < 0:
        print (sum_edges)
        face.select = True

Problem is, this code always selects the same faces no matter if i mirror any uv islands …

Hmm that’s odd, I thought that loops or in the vertex order of that face and always clockwise stored. That’s kind of the point often with 3D containers as that’s how you determine if a face is flipped.

There seems to be something here that I am missing, perhaps someone else knows more? Here is my latest snippet which makes it a bit nicer to run as it deselects UV faces first and then selects UV faces instead of mesh faces


import bpy
import bmesh


# Create BMesh from active object (e.g. selected one)
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uvLayer = bm.loops.layers.uv.verify()


print("Select flipped UV's")


# Set UV selection mode to Face
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
bpy.context.scene.tool_settings.use_uv_select_sync = False
bpy.ops.uv.select_all(action='DESELECT')


for face in bm.faces:
	sum = 0
	
	count = len(face.loops)
	
	for i in range(count):
		uv_A = face.loops[i].vert.co
		uv_B = face.loops[(i+1)%count].vert.co
		sum += (uv_B.x - uv_A.x) * (uv_B.y + uv_A.y)


	if sum < 0:
		print ("Select face {} ".format(sum))
		print("Face {}".format(type(face)))
		for loop in face.loops:
			loop[uvLayer].select = True

Sorry if I can’t help any further for now. When I have more time I’ll try to investigate more.

I totally agree with you. I’ve also read everywhere that the vertex order determines wether UVs are inverted or not. Maya even has a tool for that where it is also explained: https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2015/ENU/Maya/files/Editing-UVs-Display-UV-winding-order-htm.html

I wonder why Blender does not provide a tool or an addon. And all because you still cannot bake procedural created Normal-Maps from Cycles :smiley: … Well anyways … Here is what i want to do in general: I created a texture bake-tool that bakes all neccessary textures for PBR at once and combines multi-material objects into one texture. So if you join multiple objects with different procedural Cylces-textures into one object you can easily bake them down to textures. It even bakes subsurface-, refraction- and alpha masks without the need to change the material-node in cycles. Works pretty well except for procedural generated Normalmaps AND faces with mirrored UVs. I can fix this problem if i flip the faces with mirrored UV coords before baking and flip them back when this process finished. Now why is this even happening? I think (and this is just guesswork but some tests seemed to proof it - correct me if iam wrong):

1: Say you have a mirrored highpoly object. Then you retopo and UV-unwrap it. AFTER THAT you mirror it (e.g. with mirror-modifier und don’t flip U or V coords) -> The mirrored version will get inverted UV coords (because you flipped your mesh, but not it’s according UVs i guess). Now you just drag them to their unique space on the UV Map.

2: Now you bake your normals to a texture and then there is something strange in my oppinion: The baked normals of both islands (the one which is clockwise and the counter-clockwise) look totally the same - no flipped green channel what i would estiamte. That leads to the assumption that blender internally (not only in shading modes) does this flipping internally during bake like gameengines do too.

3: And now in my final step i take this normals and bake them using this node constellation: https://blenderartists.org/forum/showthread.php?348799-Baking-Cycles-Procedural-Normals-to-Texture . It calculate from object-space to tangent-space and uses the UV-tangents and Face-Normals to do so. In this process there is no automatic flipping of the green channel (like in step 3) - and that results into wrong normal information of faces with mirrored UVs.

The solution is to flip faces with mirrored UVs before the baking and flip them bake when baking is done but the actual problem is to find out which faces have mirrored uvs and must be flipped and which does not.

Sorry for that essay but that might help you to understand the whole need for it and maybe you see a completely different way to solve this. Thanks for your time dude!

Hey there, i got it… funny (and somehow sad ^^) story. The mistake was we read the geometry face-vertices not the UV-face-vertices. Seems i missed to read the documentation for the BMLoop module well :D:D

Wrong:


for i in range(3):
        uv_A = face.loops[i].vert
        uv_B = face.loops[(i+1)%3].vert
        sum_edges += (uv_B.co.x - uv_A.co.x) * (uv_B.co.y + uv_A.co.y)

Correct:


for i in range(3):
        uv_A = face.loops[i][uvLayer].uv
        uv_B = face.loops[(i+1)%3][uvLayer].uv
        sum_edges += (uv_B.x - uv_A.x) * (uv_B.y + uv_A.y)


Working code is:


import bpy
import bmesh


# Create BMesh from active object (e.g. selected one)
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uvLayer = bm.loops.layers.uv.verify()


for face in bm.faces:
    sum_edges = 0
    # Only loop 3 verts ignore others: faster!
    for i in range(3):
        uv_A = face.loops[i][uvLayer].uv
        uv_B = face.loops[(i+1)%3][uvLayer].uv
        sum_edges += (uv_B.x - uv_A.x) * (uv_B.y + uv_A.y)
        
    if sum_edges > 0:
        print (sum_edges)
        face.select = True

Thank you dude, works like a charm :slight_smile:

Ooops ._. how could i have missed that. Ha ha great now i can implement this into TexTools as well an operator to select flipped uv faces.
Thank you as well :slight_smile:

On a side note: This is what I ended up using it for in TexTools

A ‘Select Flipped Islands’ operator
https://farm5.staticflickr.com/4758/39571756815_62613c3461_o.gif