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

    Generating Hair [SOLVED]

    SDK Help
    0
    9
    966
    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.
    • H
      Helper
      last edited by

      On 24/03/2015 at 06:55, xxxxxxxx wrote:

      User Information:
      Cinema 4D Version:   R16 
      Platform:   Windows  ;   
      Language(s) :     C++  ;

      ---------
      Hi all,

      I'm working on a c++ plugin which extracts certain types of scene data. Long story short, I have a HairObject from the scene and want the curves for each generated hair strand. For instance, with a hair object that has HAIRSTYLE_HAIR_COUNT = 1000 and HAIRSTYLE_HAIR_SEGMENTS = 3, I'd end up with 4000 points arranged into segments.

      If I force generation by setting HAIRSTYLE_GENERATE to HAIRSTYLE_GENERATE_SPLINE (through the hair object's properties panel in C4D) I get the results I want as a LineObject in the hairobject's cache. This data works fine for my purposes. However, I don't want a theoretical user of my plugin to have to do this manually. For one thing, it tends to kill C4D performance on the one large example I have here. I would like to generate this data on demand when the plugin command is launched.

      I was hoping the HairObject::GenerateHair function would do the trick, but I haven't been able to pass it any arguments that don't result in a crash. Besides which, it seems to generate a HairGuides object rather than the LineObject I would expect.

      Hope this is possible, and that someone out there has experience with this! Thanks in advance,
      - Sean

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 24/03/2015 at 08:30, xxxxxxxx wrote:

        Hello,

        The cache is only filled when a generator is used inside a document. So one way could be to create a virtual BaseDocument, copy that hair object and all dependencies into that document and call ExecutePasses(). This way you can do to the Hair object anything you like.

        Another way is indeed to use GenerateHair(). To use that function you have to lock the hair object. The returned HairGuides object than also contains the data for all hair strands if that data was created. This can look like this:

          
        HairObject * hair = (HairObject* )object;   
          
        hair->Lock(doc, nullptr, false, 0);                
        HairGuides * guides = hair->GenerateHair();              
        hair->Unlock();                    
          
        const Int32 pointCount = guides->GetPointCount();           
        const Int32 segPointCount = guides->GetGuidePointCount();     
          
        Vector* hairpoints = guides->GetPoints();                         
          
        BaseObject * parent = nullptr;      
          
        for(Int32 pointIndex = 0; pointIndex < pointCount; ++pointIndex)           
        {                  
          Int32 localIndex = pointIndex % segPointCount;  
          
          if(localIndex == 0)                   
          {                       
              // a new guide starts  
          }      
          
          const Vector pos = hairpoints[pointIndex];      
        }  
        

        best wishes,
        Sebastian

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 25/03/2015 at 06:32, xxxxxxxx wrote:

          This is perfect, thanks a lot Sebastian!

          Now I'm trying to retrieve the color and thickness information. I'm not having much luck with the hair materials. I've got the BaseTag of type HAIRMATERIAL_TAG_LINK and get the HairMaterialData pointer with GetParameter/GetLink. I don't really know where to go from here, though.

          The GetColor and GetThickness methods on the HairMaterialData are kind of daunting, and seem like overkill somehow. It seems like I should be able to call GetParameter on some object with HAIRMATERIAL_THICKNESS_ROOT/TIP and HAIRMATERIAL_COLOR_GRADIENT to get the colors.

          Could you give a little example of how to use the methods on HairMaterialData, or how to access this information through GetParameter somehow?

          Thanks again,
          - Sean

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 25/03/2015 at 11:21, xxxxxxxx wrote:

            Hello,

            actually, you can get the HairMaterialData object when you call GenerateHair() (the fourth parameter). Using GetParmeter() etc. you can only read the parameters; the hair color can be defined by a shader and may vary from hair to hair. So I think you have to GetThickness() and GetColor() as these functions are made to get the proper values for each hair.

            Best wishes,
            Sebastian

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 26/03/2015 at 05:12, xxxxxxxx wrote:

              Thanks again,

              I hadn't realized the HairMaterialData parameter was an output parameter for GenerateHairs. That might be handy. As I say, though, I have already found pointers to the correct materials by following the HAIRMATERIAL_TAG_LINK on the hair material BaseTag objects of the hair.

              I've messed around with GetThickness and GetColor and I still can't quite work them out.
              GetColor needs world coordinates of the root and the sample point on the hair, as well as the surface normal at the root and a RayHitId.
              I can't even work out how to retrieve the color on an extremely simple example. For instance, I have a few hairs on a plane object and I thought I would be able to get the color at the base of the first hair with something like:

              Vector color = hairMat->GetColor(0, 0.0, guides->GetPoints()[0], guides->GetPoints()[0], Vector(0,0,0), Vector(0,1,0), nullptr, RayHitID(), -1);
              

              This does give me a color (a light brown), but it does not appear to have any relation to either of the colors in the material property gradient (in the example I use, they are set to orange and red). Changing the t parameter to 1.0 instead of 0.0 does not seem to make a difference.

              I had hoped that GetThickness would give better results, but this method seems to return 1.0 regardless of input. For instance, I would have imagined that the following loop would give the "root" and "tip" thickness values from the material:

                            for(int i=0; i<guideCount; i++) {  
                            root = hairMat->GetThickness(i, 0.0f);  
                            tip = hairMat->GetThickness(i, 1.0f);  
                          }  
              

              Instead it seems I get nothing but 1.0.

              By messing around a bit I did actually succeed in retrieving parameters from the material properties with GetParameter. I was previously put off by the fact that the HairMaterialData class does not inherit GetParameter from C4DAtom. The SDK still confuses me like this sometimes.

              So I now have more or less the values I want. However, it does seem like some of the methods in HairMaterialData would be useful to let C4D calculate some of the more complex material assignment cases for me, so any comments on the problems I describe above would be much appreciated.

              Regards,
              - Sean

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 26/03/2015 at 07:37, xxxxxxxx wrote:

                Hello,

                using the HairMaterialData object from GenerateHair() I have no problem with GetThickness(). Also GetColor() seems to work mostly. Since these methods are made to be used in a rendering context it can be tricky to construct the right parameters. But it seems that something like this can also work to get at least the basic color for each hair:

                  
                color = materialData->GetColor(counter,guidePos,Vector(),Vector(),Vector(),Vector(),nullptr, RayHitID());  
                

                best wishes,
                Sebastian

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 10/10/2015 at 12:52, xxxxxxxx wrote:

                  Hi,
                  I'm posting my issue to this post due to don't want to open a new discussion about this.

                  Atm i'm using GenerateHair() to produce hairs out of video post. But i don't have any success to get proper UV per hair strands.

                  guides->GetRootUV(i) is giving ZERO for each one. But it's fine when i use in videopost. Can you help for this problem?
                  
                  
                    
                  
                  
                  
                  Thanks!
                  
                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 12/10/2015 at 02:48, xxxxxxxx wrote:

                    Hello,

                    if I use GetRootUV() between Lock() and Unlock() it seems to work fine.

                    Best wishes,
                    Sebastian

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 12/10/2015 at 08:25, xxxxxxxx wrote:

                      Originally posted by xxxxxxxx

                      Hello,

                      if I use GetRootUV() between Lock() and Unlock() it seems to work fine.

                      Best wishes,
                      Sebastian

                      😊

                      Wow! You saved my life, thanks!

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