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

    How to access PLA data

    Cinema 4D SDK
    c++
    2
    19
    2.6k
    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.
    • ferdinandF
      ferdinand @facumh
      last edited by

      Hey @facumh,

      Okay, great to hear! When you run into problems, just return with the concrete code you are stuck with (so that I can compile it), I will help you then. When you do not want to share your full code publicly, you can also use our contact form or send us a mail to sdk_support(at)maxon(dot)net.

      Cheers,
      Ferdinand

      MAXON SDK Specialist
      developers.maxon.net

      1 Reply Last reply Reply Quote 0
      • F
        facumh
        last edited by

        Hey ferdinand,
        unfortunately I can not share the whole code, since it is a large and complex project. But hopefully with these small fractions you can help me identify where is the mistake.
        Firstly, as you correctly guessed the plugin hook is an ObjectData.

        As for the mistake of using CTpla for the second argument of DescLevel, that is shows in the CTrack Manual, maybe a comment could be added whether that for an outdated version of the SDK, or something else.

        Thirdly, I've made the change you suggested to the parameters to getting plaID, but now both FindCTrack and CTrack::Alloc( obj, plaID ); are returning null. The first one is expected since on the first time executing this part of the code there shouldn't be any tracks but I find it odd that the Alloc is also returning nullptr (I've checked that there is plenty of memory on the system, so at least that is not the case).

        Alternatively, if I use CTpla in both parameters (they way it's suggeested on the CTrack Manual), I noticed that when calling CCurve* curve = plaTrack->GetCurve(); I get a non-nullptr back, but the underlying base2Dobject, is nullptr, is this also expected?

        Thanks for your time and response.
        Cheers,
        Facundo

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

          Hey @facumh,

          sorry for sending you on the wrong track, the so called special tracks seem to pass the track ID and not its data type the data type argument of a DescLevel, so what you did was correct.

          Find below my Python code translated to C++ (for 2024),

          Cheers,
          Ferdinand

          Result:
          0c9c4fbc-d315-494e-9710-a6ca48039110-image.png

          Code:

          /// Demonstrates reading PLA data in 2024.
          
          #include "c4d_basedocument.h"
          #include "c4d_baselist.h"
          #include "c4d_canimation.h"
          #include "c4d_general.h"
          #include "customgui_pla.h"
          #include "lib_description.h"
          
          #include "ckpla.h"
          
          Bool GetPlaData(BaseDocument* doc)
          {
            // Input validation.
            if (!doc)
              return false;
          
            // Since this method is called "Get", and with the constness changes of 2024, we should get here
            // a const BaseObject* and subsequently, const track, curve, key, gedata, and pladata. But because
            // PlaData::GetVariableTags is not a const method this cascades back up to here, and we must get
            // mutable data although we only want to read.
            BaseObject* const node = doc->GetActiveObject();
            if (!node || !node->IsInstanceOf(Opoint))
              return false;
            
            // Construct the ID for the PLA track and get the track and its curve.
            const DescID plaTrackId = ConstDescID(DescLevel(CTpla, CTpla, 0));
            CTrack* const track = node->FindCTrack(plaTrackId);
            if (!track)
              return false;
          
            CCurve* const curve = track->GetCurve();
            if (!curve)
              return false;
          
            // Iterate over all keys in the curve,
            for (Int32 i = 0; i < curve->GetKeyCount(); i++)
            {
              CKey* const key = curve->GetKey(i);
              if (!key)
                continue;
          
              // Get the custom data type stored in this key, in this case the PLAData. Just calling CKey::
              // GetGeData does not work here and instead we have to call this little ::GetParameter 
              // monstrosity below.
              GeData data;
              if (!key->GetParameter(ConstDescID(DescLevel(CK_PLA_DATA, CUSTOMDATATYPE_PLA, 0)), data, 
                  DESCFLAGS_GET::NONE))
                continue;
              PLAData* const pla = data.GetCustomDataTypeWritable<PLAData>();
          
              // Get the two tags which hold the actual data.
              PointTag* pointTag = nullptr;
              TangentTag* tangentTag = nullptr;
              pla->GetVariableTags(pointTag, tangentTag);
          
              // This is a critical event, a PLA key should always hold point data, we should raise some kind 
              // of error here.
              if (!pointTag)
                continue;
          
              // Get the read-only vectors and iterate over all of them.
              const Vector* points = pointTag->GetDataAddressR();
              for (Int32 j = 0; j < pointTag->GetDataCount(); j++)
                ApplicationOutput(
                  "Pla point data for t='@' and index='@': @", key->GetTime().Get(), j, points[j]);
          
              // This is not a critical event, only PLA data for splines holds tangent data.
              if (!tangentTag)
                continue;
          
              // Iterate over the read-only tangents.
              const Tangent* tangents = tangentTag->GetDataAddressR();
              for (Int32 k = 0; k < tangentTag->GetDataCount(); k++)
                ApplicationOutput(
                  "Pla tangent data for t='@' and index='@': (vl=@, vr=@)", key->GetTime().Get(), k, 
                  tangents[k].vl, tangents[k].vr);
            }
          
            return true;
          }
          

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • F
            facumh
            last edited by

            hey @ferdinand,
            thanks for all the help. This code was very helpful, but I still get the same points, from different keys even if I create them with different times. Which makes me wonder if maybe I am creating them with the wrong parameters. I also tried using just one key and moving frame by frame, but still the points from the pointTag remained always the same.
            Cheers, Facundo

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

              Hey @facumh,

              As you can see in my screenshot, my code is reading the correct data for the two keys in the scene. Are you also creating the keys programmatically, or have the keys been created manually? Without your code, it will be hard for me to help you here.

              Cheers,
              Ferdinand

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 0
              • F
                facumh
                last edited by

                Hey @ferdinand,
                in my second comment you can see how I create the keys, programmaticallly. I know it's hard without the whole code, but so far this has being very educational for me as well, just wanted to thank you for that as well.
                Cheers, Facundo

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

                  Hey @facumh,

                  your code looks okay, at least when one wants to rely on FillKey (which we internally also do). The other route would be to do the inverse of what I showed in my code and manually write the arrays of the underlying PLAData.

                  There are however two problems with your code, you use CTrack::FillKey which "Fills key with default values." But looking at our PLA baking code, this seems to respect the points and tangents of the passed bl, i.e., is smart enough to know what to do with the special track PLA.

                    if ((flags & TL_FLAG_BAKE_PLA || flags & TL_FLAG_BAKE_ALL) && source->IsInstanceOf(Obase))
                    {
                      desttrack = nullptr;
                      if (bSearchTrack)
                        desttrack = dest->FindCTrack(ConstDescID(DescLevel(CTpla, CTpla, 0)));
                      if (!desttrack)
                      {
                        desttrack = CTrack::Alloc(dest, ConstDescID(DescLevel(CTpla, CTpla, 0)));
                        GeListHead* head = dest->GetCTrackRoot(true);
                        if (!head)
                          return false;
                        head->InsertLast(desttrack);
                      }
                      if (desttrack)
                      {
                        pKey = desttrack->GetCurve()->AddKey(time, &idx);
                        if (pKey)
                        {
                          desttrack->FillKey(doc, source, pKey);
                  
                          if (flags & TL_FLAG_BAKE_CLEAN)
                            CleanKeys(*desttrack, idx, *pKey, isLastFrame);
                        }
                      }
                    }
                  

                  There are however two other problems, first of all, you do not make sure to write to two unique time values, when BaseDocument::GetTime()::Get() == 0.0, then you will write to the same key. You also pass for both keys obj as the bl, creating the same default values for both keys.

                  This is then likely the reason for you reading the same data on both keys.

                  Cheers,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • F
                    facumh
                    last edited by

                    hey @ferdinand ,
                    I did suspect using objfor both keys could be the issue, but then how would you suggest I extract the animated spline from the baseDoc ?

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

                      Hey @facumh,

                      My point was that when you have an object obj whose points (and tangents) are in the state X and you use that object to fill the value(s) for a key, you will obviously end up with identical key values.

                      So, you have to either manually write the key data, I already hinted at the fact that you could do that by doing the inverse of what I showed, to manually write the point and tangent data stored in a PLA key, or alternatively, you could change the state of obj, so that its points and tangents are not in the state X anymore. Otherwise, both keys will be filled with the state X.

                      Cheers,
                      Ferdinand

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • F
                        facumh
                        last edited by facumh

                        hey @ferdinand ,
                        sorry I should have explained a little more. So the first time this function is executed, there are no tracks, so I create the track and first key, based on Obj in state of time 0.0. On later executions, the doc->getTime() is no longer 0.0, and I thought this would mean that the baseObject Obj would also have the point of the spline in this new time, and since now there is already a track, I create the new key using Obj with the current time. This way and checking with key = curve->FindKey(time) and checking that it's not null, I make sure to not replicate keys with the same time hence (I thought), not two keys with the same state of `Obj.

                        Thanks for your time and patience with all my doubts.
                        Cheers, Facundo

                        Edit: sorry, not sure if it's clear, but what want is the position of the spline points in different moments of the animation and I thought with the pla tracks and keys this info could be retrieveed

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

                          Hey @facumh,

                          I understand what you want to do, you want to record a PLA key for the given object at the given document time. For which you could btw also just use the CallCommand for Record Active Objects:

                          59287ed1-21d1-4dbb-9c7a-30833e88428a-image.png

                          Just turn off position, rotation, scale, and parameter recording, and turn on PLA (just as shown in the screen above), then invoke "record active objects". Should be doable in less than 15 lines, including reverting to the previous animation recording state. You can use CallCommand to invoke all 6 buttons here. You can use IsCommandChecked to find out if a command is "blue" or not, so that you know when you must run CallCommand to toggle the state and when not.

                          Regarding your code, you showed us above that you write two keys:

                          auto const time = doc->GetTime();
                          const DescID plaID = DescLevel( CTpla, CTpla, 0 );
                          CTrack* plaTrack = obj->FindCTrack( plaID );
                          if( plaTrack == nullptr )
                          {
                          	plaTrack = CTrack::Alloc( obj, plaID );
                          	if( plaTrack == nullptr )
                          		throw maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
                          	obj->InsertTrackSorted( plaTrack );
                          	CCurve* curve1 = plaTrack->GetCurve();
                          	if( curve1 == nullptr )
                          		throw maxon::UnexpectedError( MAXON_SOURCE_LOCATION );
                          	// set first key
                          	CKey* const key1 = curve1->AddKey( BaseTime( 0.0 ) );
                          	if( key1 == nullptr )
                          		throw maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
                          	plaTrack->FillKey( doc, obj, key1 );
                          }
                          // set second key
                          CCurve* curve = plaTrack->GetCurve();
                          auto animatedParams = curve->GetInfo();
                          CKey* const key2 = curve->AddKey( time );
                          if( key2 == nullptr )
                          		throw maxon::OutOfMemoryError( MAXON_SOURCE_LOCATION );
                          plaTrack->FillKey( doc, obj, key2 );
                          GeData curveParams;
                          auto foundParams = curve->GetParameter( plaID, curveParams, DESCFLAGS_GET::NONE );
                          auto geDataCurve = key2->GetGeData();
                          

                          If you use other code, you should show us this, in fact you should always post executable code, as things otherwise tend to get convoluted 😉

                          Cheers,
                          Ferdinand

                          PS: When I have time I will squeeze in a code example for recording the PLA state of the selected object at the current document time. But it might take me some time, I am a bit busy at the moment.

                          MAXON SDK Specialist
                          developers.maxon.net

                          1 Reply Last reply Reply Quote 0
                          • F
                            facumh
                            last edited by

                            Hi @ferdinand ,
                            thanks for the tip of using Record Active Element.
                            On one hand I couldn't find the command id on the sdk website.
                            On the other hand, playing around with the RAE and the spline, I realized that whenever the active element is the spline and I press play, it doesn't move, but when it is not the active element, and I press play, then the animation moves. I'm using a rope expresion tag to animate it and rope belt tag to fixate one end of the spline.
                            This behaviour of the spline not moving when it's the active element would explain why I always get the same points on the different keys, even when the animation moves. But could you maybe explain this behaviour ?

                            Cheers, Facundo

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

                              Hey @facumh,

                              Not all command IDs are exposed as symbols. The easiest way to find out their ID is the so-called Script Log. Simply invoke the actions you are interested in, and the look into the log. The Script Log is by default part of the Script layout.

                              7bdf72c9-47bf-493b-9610-90860e9560b6-image.png

                              Regarding your other question(s), these are unfortunately out of scope of support here, as they are end-user questions. I must ask you to direct them towards our end-user support via Support Portal: Support Request.

                              Thank you for your understanding,
                              Ferdinand

                              MAXON SDK Specialist
                              developers.maxon.net

                              1 Reply Last reply Reply Quote 0
                              • F
                                facumh
                                last edited by

                                Hi @ferdinand ,
                                Thanks very much for all of your time and tips!!
                                Facundo

                                1 Reply Last reply Reply Quote 0
                                • i_mazlovI i_mazlov referenced this topic on
                                • First post
                                  Last post