@ferdinand said in GetActiveUVSet() returns None if multiple UVs are active?:
Unfortunately, your question is also a bit ambiguous, as you do not explain what you would consider valid UVW data in a '(multiple tags/objects selected in OM)'. A c4d.modules.bodypaint.TempUVHandle represents the data of a singular UVW map, so if you want to access or modify the data of multiple tags you must iterate over them yourself.
Aye, that's the info I needed. I wanted to process only the UVs that show up in the Texture/UV Editor. So if the Object Manager has more than one UV tag selected on a single object, or if a Material tag is selected, the code would ignore the selected tags that aren't active.
For example in the screenshot below, CubeA has two UV tags selected, and CubeB has its Material tag selected. Only CubeA's first UV tag and CubeB's second UV tag show up in the Texture/UV Editor, so only those two tags would be processed.
I adjusted the code you gave me to only work on the Material/UV tags that show up in the Texture/UV Editor. (I might modify it later to factor in the Material tag's Offset/Length/Tile values)
So far it's doing exactly what I need. Only issue I have is if I need to undo the script. I have to press the Undo button several times because C4D creates an undo state for every Tag/Object selection step in the script, instead of putting all of them under one undo state. I think this is a long-existing limitation of C4D's Undo system?
"""Name-en-US:snap uv to left
Description-en-US:sets selected UV points to left edge of UV tile
"""
import c4d
from c4d.modules.bodypaint import GetActiveUVSet, UpdateMeshUV, TempUVHandle
#==============================================================================
# Only one UV tag can be active per object.
# If an object has multiple UV/Material tags, only one UV is displayed in the Texture Editor no matter how many tags are selected.
# If multiple UV/Material tags are selected on one object, the leftmost tag is used
def GetActiveUVTags():
# Collect selected uvw and material tags
selected_tags = [tag for tag in doc.GetActiveTags() if tag.CheckType(c4d.Tuvw) or tag.CheckType(c4d.Ttexture)]
active_objects:list[c4d.BaseObject] = []
active_uv_tags:list[c4d.BaseTag] = []
# Only pick first material/uv tag in an object, ignore the rest
for selected_tag in selected_tags:
if selected_tag.GetObject() in active_objects:
#If active tag of an object has been found, skip
continue
if selected_tag.CheckType(c4d.Tuvw):
# First selected UV tag on the object, skip the rest
active_objects.append(selected_tag.GetObject())
active_uv_tags.append(selected_tag)
elif selected_tag.CheckType(c4d.Ttexture):
# First selected Material tag on the object, look for the UV it uses.
# If a Material tag is selected, the active UV is the first UV tag to the right of the material tag.
# If there is no UV tag to the right of the Material tag, the leftmost UV tag is active.
active_uv_tag:c4d.BaseTag = None
tag = selected_tag.GetNext()
while tag:
# Look for UV tag to the right of the Material tag
if tag.CheckType(c4d.Tuvw):
active_uv_tag = tag
break
tag = tag.GetNext()
if active_uv_tag is None:
# If there's no tag to the right of the Material tag, look for first UV tag on object
active_uv_tag = selected_tag.GetObject().GetTag(c4d.Tuvw)
if active_uv_tag:
active_objects.append(selected_tag.GetObject())
active_uv_tags.append(active_uv_tag)
# Get selected objects that have no selected tags
selected_objects = [object for object in doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN) if object not in active_objects]
for selected_object in selected_objects:
# Get first UV tag of object
tag = selected_object.GetTag(c4d.Tuvw)
if tag:
active_uv_tags.append(tag)
print(selected_objects)
print(active_uv_tags)
return active_uv_tags
def SetPoints( sel, uv ):
for i, selected in enumerate(sel):
if not selected:
continue
uv[i//4][list(uv[i//4].keys())[i%4]][0] = 0.0
return uv
def RestoreOldSelection(old_objects, old_tags):
if old_objects:
doc.SetActiveObject(old_objects[0], c4d.SELECTION_NEW)
for obj in old_objects[1:]:
doc.SetActiveObject(obj, c4d.SELECTION_ADD)
if old_tags:
doc.SetActiveTag(old_tags[0], c4d.SELECTION_NEW)
for obj in old_tags[1:]:
doc.SetActiveTag(obj, c4d.SELECTION_ADD)
return
#==============================================================================
def main():
old_objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
old_tags = doc.GetActiveTags()
active_uv_tags = GetActiveUVTags()
# Deselect objects, only have one tag active at a time
doc.StartUndo()
for tag in active_uv_tags:
doc.AddUndo(c4d.UNDOTYPE_BITS, tag.GetObject())
doc.AddUndo(c4d.UNDOTYPE_BITS, tag)
doc.SetActiveObject(tag.GetObject())
doc.SetActiveTag(tag)
UpdateMeshUV(True)
handle: TempUVHandle | None = GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
if not handle:
raise RuntimeError("Could not access UVW handler.")
handle = GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
uv_sel = handle.GetUVPointSel()
uv = handle.GetUVW()
sel = uv_sel.GetAll(handle.GetPolyCount()*4)
handle.SetUVW( SetPoints( sel, uv ) )
# restore previous object/tag selection before UV processing
RestoreOldSelection(old_objects, old_tags)
doc.EndUndo()
c4d.EventAdd()
if __name__=='__main__':
main()