Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. ELJeffery
    E
    • Profile
    • Following 0
    • Followers 0
    • Topics 2
    • Posts 5
    • Best 1
    • Controversial 0
    • Groups 0

    ELJeffery

    @ELJeffery

    2
    Reputation
    4
    Profile views
    5
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    ELJeffery Unfollow Follow

    Best posts made by ELJeffery

    • RE: Polygon Islands Convenience Method?

      Here is something I recently used to count UV Islands as well as get some other info

      import c4d
      from collections import defaultdict
      
      def get_uv_islands(obj, tol=1e-5):
          """
          Compute UV islands for the given polygon object using its UVW tag.
          This version retrieves the UV dictionary for each polygon using uvTag.GetSlow(polyIndex),
          then groups connected polygons (sharing at least one UV coordinate) into islands
          using an iterative flood-fill algorithm.
          """
          uvw_tag = obj.GetTag(c4d.Tuvw)
          if uvw_tag is None:
              c4d.gui.MessageDialog("The object does not have a UVW tag.")
              return None
      
          poly_count = obj.GetPolygonCount()
      
          # Build maps:
          # - face_to_uvs: mapping from polygon index to its set of rounded UV keys.
          # - uv_to_faces: reverse mapping from each UV key to the set of polygon indices using that UV.
          face_to_uvs = defaultdict(set)
          uv_to_faces = defaultdict(set)
      
          def uv_key(vec):
              # Round UV vector components to mitigate floating-point precision issues.
              return (round(vec.x, 5), round(vec.y, 5))
      
          # Build connectivity maps based on each polygon's UV data.
          for poly_index in range(poly_count):
              poly = obj.GetPolygon(poly_index)
              uv_data = obj.GetTag(c4d.Tuvw).GetSlow(poly_index)
              # If the polygon is a triangle, remove the extraneous 'd' key.
              if poly.IsTriangle() and 'd' in uv_data:
                  del uv_data['d']
              for key in uv_data:
                  uv_vec = uv_data[key]
                  key_tuple = uv_key(uv_vec)
                  face_to_uvs[poly_index].add(key_tuple)
                  uv_to_faces[key_tuple].add(poly_index)
      
          # Use an iterative flood-fill to group connected faces.
          islands = []
          faces_left = set(range(poly_count))
      
          while faces_left:
              island = []
              start_face = next(iter(faces_left))  # Pick an arbitrary face from unvisited ones.
              stack = [start_face]
              while stack:
                  face_idx = stack.pop()
                  if face_idx not in faces_left:
                      continue
                  faces_left.remove(face_idx)
                  island.append(face_idx)
                  # For every UV key in this face, add all neighboring faces.
                  for uv_val in face_to_uvs[face_idx]:
                      for neighbor in uv_to_faces[uv_val]:
                          if neighbor in faces_left:
                              stack.append(neighbor)
              islands.append(island)
      
          return islands
      
      def get_island_uv_center(obj, uvw_tag, island):
          """
          Given a polygon object, its UVW tag, and an island (list of polygon indices),
          compute the UV bounding box and center.
          """
          # Initialize min/max with infinities.
          min_u = float('inf')
          max_u = -float('inf')
          min_v = float('inf')
          max_v = -float('inf')
          # Gather unique UV coordinates from all faces in the island.
          unique_uvs = {}
          for face_idx in island:
              poly = obj.GetPolygon(face_idx)
              uv_data = uvw_tag.GetSlow(face_idx)
              if poly.IsTriangle() and 'd' in uv_data:
                  del uv_data['d']
              for key in uv_data:
                  uv = uv_data[key]
                  key_tuple = (round(uv.x, 6), round(uv.y, 6))
                  unique_uvs[key_tuple] = uv
      
          # Compute the bounding box.
          for uv in unique_uvs.values():
              if uv.x < min_u:
                  min_u = uv.x
              if uv.x > max_u:
                  max_u = uv.x
              if uv.y < min_v:
                  min_v = uv.y
              if uv.y > max_v:
                  max_v = uv.y
      
          center_u = (min_u + max_u) / 2.0
          # Invert V axis so the center is correct in UV space.
          center_v = 1.0 - ((min_v + max_v) / 2.0)
      
          return center_u, center_v, min_u, max_u, min_v, max_v
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          obj = doc.GetActiveObject()
          if obj is None:
              c4d.gui.MessageDialog("Please select a polygon object.")
              return
      
          uvw_tag = obj.GetTag(c4d.Tuvw)
          if uvw_tag is None:
              c4d.gui.MessageDialog("The object does not have a UVW tag!")
              return
      
          islands = get_uv_islands(obj)
          if islands is None:
              return
      
          num_islands = len(islands)
          print("Number of UV islands:", num_islands)
          for idx, island in enumerate(islands):
              center_u, center_v, min_u, max_u, min_v, max_v = get_island_uv_center(obj, uvw_tag, island)
              print("Island", idx, "has faces:", island)
              print("Island Num",idx,"    Bounding Box: U [{:.6f}, {:.6f}], V [{:.6f}, {:.6f}]".format(min_u, max_u, min_v, max_v))
              print("                 Center: U = {:.6f}, V = {:.6f}".format(center_u, center_v))
      
      if __name__=='__main__':
          main()
      

      Output looks something like

      Number of UV islands: 2
      Island 0 has faces: [0, 5, 3, 4]
      Island Num 0     Bounding Box: U [0.004902, 0.495098], V [0.004902, 0.740196]
                      Center: U = 0.250000, V = 0.627451
      Island 1 has faces: [1, 2]
      Island Num 1     Bounding Box: U [0.004902, 0.495098], V [0.750000, 0.995098]
                      Center: U = 0.250000, V = 0.127451
      

      Hope it is useful!

      posted in Cinema 4D SDK
      E
      ELJeffery

    Latest posts made by ELJeffery

    • RE: Polygon Islands Convenience Method?

      Here is something I recently used to count UV Islands as well as get some other info

      import c4d
      from collections import defaultdict
      
      def get_uv_islands(obj, tol=1e-5):
          """
          Compute UV islands for the given polygon object using its UVW tag.
          This version retrieves the UV dictionary for each polygon using uvTag.GetSlow(polyIndex),
          then groups connected polygons (sharing at least one UV coordinate) into islands
          using an iterative flood-fill algorithm.
          """
          uvw_tag = obj.GetTag(c4d.Tuvw)
          if uvw_tag is None:
              c4d.gui.MessageDialog("The object does not have a UVW tag.")
              return None
      
          poly_count = obj.GetPolygonCount()
      
          # Build maps:
          # - face_to_uvs: mapping from polygon index to its set of rounded UV keys.
          # - uv_to_faces: reverse mapping from each UV key to the set of polygon indices using that UV.
          face_to_uvs = defaultdict(set)
          uv_to_faces = defaultdict(set)
      
          def uv_key(vec):
              # Round UV vector components to mitigate floating-point precision issues.
              return (round(vec.x, 5), round(vec.y, 5))
      
          # Build connectivity maps based on each polygon's UV data.
          for poly_index in range(poly_count):
              poly = obj.GetPolygon(poly_index)
              uv_data = obj.GetTag(c4d.Tuvw).GetSlow(poly_index)
              # If the polygon is a triangle, remove the extraneous 'd' key.
              if poly.IsTriangle() and 'd' in uv_data:
                  del uv_data['d']
              for key in uv_data:
                  uv_vec = uv_data[key]
                  key_tuple = uv_key(uv_vec)
                  face_to_uvs[poly_index].add(key_tuple)
                  uv_to_faces[key_tuple].add(poly_index)
      
          # Use an iterative flood-fill to group connected faces.
          islands = []
          faces_left = set(range(poly_count))
      
          while faces_left:
              island = []
              start_face = next(iter(faces_left))  # Pick an arbitrary face from unvisited ones.
              stack = [start_face]
              while stack:
                  face_idx = stack.pop()
                  if face_idx not in faces_left:
                      continue
                  faces_left.remove(face_idx)
                  island.append(face_idx)
                  # For every UV key in this face, add all neighboring faces.
                  for uv_val in face_to_uvs[face_idx]:
                      for neighbor in uv_to_faces[uv_val]:
                          if neighbor in faces_left:
                              stack.append(neighbor)
              islands.append(island)
      
          return islands
      
      def get_island_uv_center(obj, uvw_tag, island):
          """
          Given a polygon object, its UVW tag, and an island (list of polygon indices),
          compute the UV bounding box and center.
          """
          # Initialize min/max with infinities.
          min_u = float('inf')
          max_u = -float('inf')
          min_v = float('inf')
          max_v = -float('inf')
          # Gather unique UV coordinates from all faces in the island.
          unique_uvs = {}
          for face_idx in island:
              poly = obj.GetPolygon(face_idx)
              uv_data = uvw_tag.GetSlow(face_idx)
              if poly.IsTriangle() and 'd' in uv_data:
                  del uv_data['d']
              for key in uv_data:
                  uv = uv_data[key]
                  key_tuple = (round(uv.x, 6), round(uv.y, 6))
                  unique_uvs[key_tuple] = uv
      
          # Compute the bounding box.
          for uv in unique_uvs.values():
              if uv.x < min_u:
                  min_u = uv.x
              if uv.x > max_u:
                  max_u = uv.x
              if uv.y < min_v:
                  min_v = uv.y
              if uv.y > max_v:
                  max_v = uv.y
      
          center_u = (min_u + max_u) / 2.0
          # Invert V axis so the center is correct in UV space.
          center_v = 1.0 - ((min_v + max_v) / 2.0)
      
          return center_u, center_v, min_u, max_u, min_v, max_v
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          obj = doc.GetActiveObject()
          if obj is None:
              c4d.gui.MessageDialog("Please select a polygon object.")
              return
      
          uvw_tag = obj.GetTag(c4d.Tuvw)
          if uvw_tag is None:
              c4d.gui.MessageDialog("The object does not have a UVW tag!")
              return
      
          islands = get_uv_islands(obj)
          if islands is None:
              return
      
          num_islands = len(islands)
          print("Number of UV islands:", num_islands)
          for idx, island in enumerate(islands):
              center_u, center_v, min_u, max_u, min_v, max_v = get_island_uv_center(obj, uvw_tag, island)
              print("Island", idx, "has faces:", island)
              print("Island Num",idx,"    Bounding Box: U [{:.6f}, {:.6f}], V [{:.6f}, {:.6f}]".format(min_u, max_u, min_v, max_v))
              print("                 Center: U = {:.6f}, V = {:.6f}".format(center_u, center_v))
      
      if __name__=='__main__':
          main()
      

      Output looks something like

      Number of UV islands: 2
      Island 0 has faces: [0, 5, 3, 4]
      Island Num 0     Bounding Box: U [0.004902, 0.495098], V [0.004902, 0.740196]
                      Center: U = 0.250000, V = 0.627451
      Island 1 has faces: [1, 2]
      Island Num 1     Bounding Box: U [0.004902, 0.495098], V [0.750000, 0.995098]
                      Center: U = 0.250000, V = 0.127451
      

      Hope it is useful!

      posted in Cinema 4D SDK
      E
      ELJeffery
    • RE: Set UVWs for Ngons?

      @ferdinand I figured this issue out so you can mark this as solved/closed. Thank you.

      posted in Cinema 4D SDK
      E
      ELJeffery
    • RE: Set UVWs for Ngons?

      @ELJeffery
      The image:
      Issue.PNG

      posted in Cinema 4D SDK
      E
      ELJeffery
    • Set UVWs for Ngons?

      For updating the changed UVWs I am using:

      # Update UV data in Cinema 4D using updated data from RizomUV
      def update_uvs_in_cinema4d(doc, obj, poly_sizes, poly_uvw_ids, updated_uvws):
          # Retrieve the active document and object once
          doc, obj = get_active_object()
          if not obj or not isinstance(obj, c4d.PolygonObject):
              gui.MessageDialog("No Polygon Object selected.")
              return
      
          # Get the Polygon Translation Map
          poly_trans_map = obj.GetPolygonTranslationMap()
          polygon_count, ngon_map = poly_trans_map
      
          # Detect if there are ngons by checking if the ngonMap contains repeated values
          all_polygons = obj.GetAllPolygons()
          has_ngons = len(set(ngon_map)) != len(ngon_map)
          ngon_count = obj.GetNgonCount()
          print("Number of polygons including polygons inside any ngons: ", len(all_polygons))
          print("Number of polygons including ngons: ",polygon_count)
          print("Number of ngons: ",ngon_count)
          print("Number of internal ngon edges: ",len(ngon_map) - len(set(ngon_map)))
      
          uvw_tag = obj.GetTag(c4d.Tuvw)
          if not uvw_tag:
              print("No UVW Tag found.")
              return
      
          doc.SetMode(c4d.Mpolygons)
      
          handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
          if handle is None:
              c4d.CallCommand(170103)  # Open UV editor
              if c4d.API_VERSION >= 22000:
                  c4d.modules.bodypaint.UpdateMeshUV(False)
      
              handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL)
              if handle is None:
                  raise RuntimeError("No Active UVSet")
      
          uv_index = 0
          assembled_uvws = []
          adjusted_uvws = adjust_uvs(updated_uvws)  # Ensure UVs are normalized correctly
      
          for poly_idx, poly_size in enumerate(poly_sizes):
              uv_dict = {}
              uv_indices = poly_uvw_ids[uv_index:uv_index + poly_size]
              uv_index += poly_size
      
              # Handle triangles
              if poly_size == 3:
                  uv_dict = {
                      'c': c4d.Vector(adjusted_uvws[uv_indices[0] * 3], adjusted_uvws[uv_indices[0] * 3 + 1], adjusted_uvws[uv_indices[0] * 3 + 2]),
                      'b': c4d.Vector(adjusted_uvws[uv_indices[1] * 3], adjusted_uvws[uv_indices[1] * 3 + 1], adjusted_uvws[uv_indices[1] * 3 + 2]),
                      'a': c4d.Vector(adjusted_uvws[uv_indices[2] * 3], adjusted_uvws[uv_indices[2] * 3 + 1], adjusted_uvws[uv_indices[2] * 3 + 2])
                  }
      
              # Handle quads
              elif poly_size == 4:
                  uv_dict = {
                      'd': c4d.Vector(adjusted_uvws[uv_indices[0] * 3], adjusted_uvws[uv_indices[0] * 3 + 1], adjusted_uvws[uv_indices[0] * 3 + 2]),
                      'c': c4d.Vector(adjusted_uvws[uv_indices[1] * 3], adjusted_uvws[uv_indices[1] * 3 + 1], adjusted_uvws[uv_indices[1] * 3 + 2]),
                      'b': c4d.Vector(adjusted_uvws[uv_indices[2] * 3], adjusted_uvws[uv_indices[2] * 3 + 1], adjusted_uvws[uv_indices[2] * 3 + 2]),
                      'a': c4d.Vector(adjusted_uvws[uv_indices[3] * 3], adjusted_uvws[uv_indices[3] * 3 + 1], adjusted_uvws[uv_indices[3] * 3 + 2])
                  }
      
              # Handle ngons (polygons with more than 4 vertices)
              elif has_ngons:
                  # Sort the vertices properly and create the UV dict dynamically
                  alphabet = 'abcdefghijklmnopqrstuvwxyz'
                  for i in range(poly_size):
                      # Make sure to use the correct UV order
                      key = alphabet[i] if i < len(alphabet) else f'v{i}'  # Handle large ngons
                      uv_dict[key] = c4d.Vector(
                          adjusted_uvws[uv_indices[i] * 3],
                          adjusted_uvws[uv_indices[i] * 3 + 1],
                          adjusted_uvws[uv_indices[i] * 3 + 2]
                      )
      
              assembled_uvws.append(uv_dict)
      
          handle.SetUVWFromTextureView(assembled_uvws, True, True, True)
          bodypaint.FreeActiveUVSet(handle)
          c4d.EventAdd()
      

      My problem is, it works with a mesh that is only quads and tris or any combination thereof, but if the mesh contains any ngons then I get this result:
      Issue.PNG

      posted in Cinema 4D SDK s26 python
      E
      ELJeffery
    • Not able to post?

      I am trying to post an issue I am having with some python code but I keep getting:

      You are unable to access maxon.net
      Why have I been blocked?
      This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data.
      
      What can I do to resolve this?
      You can email the site owner to let them know you were blocked. Please include what you were doing when this page came up and the Cloudflare Ray ID found at the bottom of this page.
      
      Cloudflare Ray ID: 8ccf3bbd8c70a933 • Performance & security by Cloudflare
      
      posted in Cinema 4D SDK s26 python
      E
      ELJeffery