Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python 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

    Actively Link Hair Guides to a Spline or Alembic?

    Cinema 4D SDK
    r25 python
    2
    3
    554
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      bentraje
      last edited by

      Hi,

      By default, when you create a guide, you can't modify it outside the hair object.
      Works for static meshes but not really for animated/dynamics objects.

      I wanted to actively link the hair guides to separate object (i.e. spline and alembic) like the hair system of other DCCs. So that I can animate/do dynamics on the spline and perform much more feasible art direction.

      Not possible natively but was wondering if this is possible in Python?

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @bentraje
        last edited by

        Hello @bentraje,

        Thank you for reaching out to us. Your question is a bit ambiguous, but I largely understand it so that you want to control hair guide vertices programmatically, e.g., bind the vertex of a hair guide to a null object, or even more complex cases as "splines and alembic". The short answer is, no, that is not possible from Python, but it would be from C++.

        The longer answer is that while there is c4d.modules.hair in Python, you are effectively restricted to read access. You can write hair guide vertex values, and they will be reflected in a scene in a certain sense, but you lack the means (MSG_HAIR_GET_OBJECT_TYPE) to properly update the hair object using these guides. Which will then result in unrooted hair guides, duplicated guides, and finally crashes. Find an example below.

        I will update the Python docs with a warning for others not to run into the same trap I did. What you could however do, is using the Hair Object > Guides > Editing > Points Link parameter to drive the guides. This could be with one of the many deformation tools of Cinema 4D, please reach out to user support for any help with this, or using Python, find a simple example below.

        Cheers,
        Ferdinand

        Hair Module in Python

        You should not be doing what I am doing here, as this will lead to crashes.

        Scene File: python_hair_crash.c4d

        Spline Workaround

        Scene File: python_hair_spline_rig.c4d

        Result:

        Code:

        """Demonstrates how to drive hair guides with a null object.
        
        This could all be done much more elaborately, I implemented here the most simple version possible.
        """
        
        import c4d
        import typing
        
        doc: c4d.documents.BaseDocument
        op: c4d.BaseTag
        bt: typing.Optional[c4d.threading.BaseThread]
        
        ID_SPLINE_OBJECT: int = (c4d.ID_USERDATA, 1)
        ID_SPLINE_SEGMENT: int = (c4d.ID_USERDATA, 2)
        
        def GetSegmentVertexIndexRange(spline: c4d.SplineObject, index: int) -> tuple[int, int, int]:
            """Returns the vertex index range for a given segment in a spline.
        
            Args:
                spline: The spline to evaluate.
                index: The segment index to evaluate.
        
            Returns:
                The number of vertices in #index,
                The first vertex index in #index,
                The last vertex index in #index
            """
            # Validate the inputs.
            if not isinstance(spline, c4d.SplineObject):
                raise TypeError(f"{spline = }")
        
            count: int = spline.GetSegmentCount()
            if index > (count - 1):
                raise IndexError(f"Segment index {index} is out of bounds for {spline}.")
        
            # Get the start and end index by summing up the number of points in all segments up to #index.
            a, b = 0, 0
            for i in range(count):
                if i < index:
                    a += spline.GetSegment(i)["cnt"]
                    continue
                b = a + spline.GetSegment(i)["cnt"] - 1
                break
        
            return b - a, a, b
        
        def main() -> None:
            """
            """
            # Get the inputs.
            target: c4d.BaseObject = op.GetMain()
            if not isinstance(target, c4d.BaseObject):
                raise RuntimeError("Tag is not attached to an object.")
        
            spline: c4d.SplineObject = op[ID_SPLINE_OBJECT]
            if not isinstance(spline, c4d.SplineObject):
                raise TypeError(f"{spline = }")
        
            # Get the vertex index range for the given segment index, e.g., (0, 8) for the first segment or
            # (9, 17) for the second segment, for a spline with two segments, each holding eight points.
            index: int = op[ID_SPLINE_SEGMENT]
            count, a, b = GetSegmentVertexIndexRange(spline, index)
        
            # The position of the null object in the coordinate system of the spline object and all its points.
            p: c4d.Vector = ~spline.GetMg() * target.GetMg().off
            points: list[c4d.Vector] = spline.GetAllPoints()
        
            # The last point q in the segment and the delta vector from q to our target point p.
            q: c4d.Vector = points[b]
            delta: c4d.Vector = p - q
        
            # Iterate over all points in the segment and adjust their position so that points[b] == p and
            # points[a] == points[a], and all other points cubically interpolated in between.
            for segmentIndex, pointIndex in enumerate(range(a, b + 1)):
                weight: float = (float(segmentIndex) / float(count)) ** 3
                print (segmentIndex, pointIndex, weight)
                points[pointIndex] += (delta * weight)
        
            spline.SetAllPoints(points)
            spline.Message(c4d.MSG_UPDATE)
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 1
        • B
          bentraje
          last edited by

          Hi @ferdinand

          Thanks for the response and heads up on the crashes.

          RE: you want to control hair guide vertices programmatically
          Yep yep you are right on this part. Basically, have a geometric hair animated and simulated for preview. But rendered on the actual hair object.

          This is the workflow for other DCC, and the more logical one. This way you separate the hair source and hair generation. It's easier to debug.

          Anyhow, for looking at your python example, this should get me by on my current use case.
          Thanks for your illustration as always!

          Will close thread now.

          1 Reply Last reply Reply Quote 0
          • First post
            Last post