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))
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 wonder why Blender does not provide a tool or an addon. And all because you still cannot bake procedural created Normal-Maps from Cycles … 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
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