CCurve::GetValue for non-real



  • On 29/12/2015 at 09:16, xxxxxxxx wrote:

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

    ---------
    Hi,

    I'm trying to get values on a CCurve (between two keyframes).

    I managed to get the values for existing keyframes :

    DescID descID = DescID(DescLevel(id, op->GetDataInstance()->GetType(id), 0));
    CTrack *track = op->FindCTrack(descID);
    if (track)
    {
        CCurve *curve = track->GetCurve();
        CKey* key = curve->FindKey(time);
        GeData data;
        if (key->GetParameter(DescLevel(ID_CKEY_VALUE), data, DESCFLAGS_GET_0))
            return data;
    }
    

    It seems that CKey::GetValue only works for real values, CKey::GetGeData only works for non-real values and CKey::GetParameter (C4DAtom::GetParameter) works for both (not very clear in the documentation).

    For keyframes on real values, I can use curve->GetValue(time) to get an interpolated value but when I try on a BaseTime parameter it doesn't works (even if the BaseTime are interpolated).

    Is there a way to get interpolated values for non-real parameters ? Something like curve->GetGeData(time).

    I know there is an InterpolateKeys method in CustomDataTypeClass but I don't know if it's related...

    Thanks.

    Btw, there is a small typo in the doc (CKey::GetValue) : https://developers.maxon.net/docs/Cinema4DCPPSDK/html/class_c_key.html#a892d08bd0b155c2e4ae549f9a8c129f8

    if (!key-GetParameter(DescLevel(CK_PLA_DATA,CUSTOMDATATYPE_PLA,0), data, 0))
    should be
    if (!key->GetParameter(DescLevel(CK_PLA_DATA,CUSTOMDATATYPE_PLA,0), data, 0))

    It would be nice if there was an example with ID_CKEY_VALUE too.



  • On 30/12/2015 at 09:49, xxxxxxxx wrote:

    Hi,

    To get the interpolated value for non-real values you have to animate the document to the needed frame with BaseDocument::SetTime and BaseDocument::ExecutePasses.
    Then call C4DAtom::GetParameter to retrieve the parameter's value at that time.

    CustomDataTypeClass::InterpolateKeys is meant for custom data types that have to provide a special interpolation.

    Thanks for reporting the small typo in the docs for CKey::GetValue.



  • On 30/12/2015 at 11:56, xxxxxxxx wrote:

    Hi Yannick,

    Thanks for your reply.
    Your solution is working but it means that I have to calculate the whole scene for each frames (which is not acceptable).

    My initial goal was to retrieve the corresponding bitmap file for image sequences bitmap (Xbitmap) on a given frame.

    https://plugincafe.maxon.net/topic/8970/11903_getting-the-frame-from-movie-in-material-solved&KW=bitmap+animation&PID=46991#46991

    I wrote a method that return the corresponding file depending on the frame and the bitmap parameters (mode, timing, range start/end, loops, movie start/end/rate).
    But knowing that all these parameters are keyframable, if I want to get all images in a frame range, I have to run the method on each frames.

    I tried with CCurve::AddKeyAdaptTangent to add a keyframe on each frame to avoid interpolation calculation, but it doesn't works for BaseTime.

    Since the animation parameters of Xbitmap are either Int or BaseTime I end up with something like that for the interpolation calculation:

    const GeData utils::GetFrameData(BaseList2D* op, const BaseTime& time, Int32 id)
    {
        Int32 type = op->GetData().GetType(id);
        DescID descID = DescID(DescLevel(id, type, 0));
        CTrack *track = op->FindCTrack(descID);
        if (track)
        {
            CCurve *curve = track->GetCurve();
            GeData data;
            // Interpolated types
            switch (type)
            {
                case DA_LONG:
                    data.SetInt32(curve->GetValue(time));
                    return data;
                case DA_REAL:
                    data.SetFloat(curve->GetValue(time));
                    return data;
                case DA_LLONG:
                    data.SetInt64(curve->GetValue(time));
                    return data;
                case DA_TIME:
                    data.SetBaseTime(utils::GetInterpolatedValue(curve, time).GetTime());
                    return data;
            }
            // Un-interpolated types
            CKey* key = curve->FindKey(time, nullptr, FINDANIM_LEFT);
            if (key)
                return utils::GetKeyValue(key);
            key = curve->FindKey(time, nullptr, FINDANIM_RIGHT);
            if (key)
                return utils::GetKeyValue(key);
        }
        return op->GetDataInstance()->GetData(id);
    }
      
    const GeData utils::GetInterpolatedValue(CCurve* curve, const BaseTime& time)
    {
        CKey* key = curve->FindKey(time, nullptr, FINDANIM_EXACT);
        // Exact keyframe
        if (key)
            return utils::GetKeyValue(key);
        CKey* left = curve->FindKey(time, nullptr, FINDANIM_LEFT);
        CKey* right = curve->FindKey(time, nullptr, FINDANIM_RIGHT);
        // Left or right
        if (!right ^ !left)
            return utils::GetKeyValue((left ? left : right));
        // Both
        if (left && right)
        {
            GeData data = utils::GetKeyValue(left);
            if (left->GetInterpolation() == CINTERPOLATION_STEP)
                return data;
            if (data.GetType() == DA_TIME)
            {
                const Int32 dummyFps = 30;
                Float start = left->GetTime().GetFrame(dummyFps);
                Float end = right->GetTime().GetFrame(dummyFps);
                Float current = time.GetFrame(dummyFps);
                Float mix = (current - start) / (end - start);
                
                start = data.GetTime().GetFrame(dummyFps);
                end = utils::GetKeyValue(right).GetTime().GetFrame(dummyFps);
                Float interpolated = (end * mix) + (start * (1.0 - mix));
                data.SetBaseTime(BaseTime(interpolated, dummyFps));
            }
            return data;
        }
        return GeData();
    }
      
    GeData utils::GetKeyValue(CKey* key)
    {
        GeData data;
        key->GetParameter(DescLevel(ID_CKEY_VALUE), data, DESCFLAGS_GET_0);
        return data;
    }
    

    I use CCurve::GetValue for real types and a custom function for BaseTime (grabbing the previous and next frame to calculate the interpolation).
    It's not very generic but it is suitable for my needs.
    I need to test it more to see if it works in all cases.
    By the way, thank you for the tips you gave me at the C4Day regarding unit tests, I'm currently implementing unit tests with Google Test and it seems to works well!



  • On 31/12/2015 at 06:21, xxxxxxxx wrote:

    Hi Yann,

    I agree my solution isn't the best in your case but for exporters that's the one usually used.

    Yes you can calculate yourself the interpolated value but it may not work for all curves and interpolations.
    CCurve::GetTangents might be useful in your implementation.

    I'm glad you've integrated unit tests in your development.
    Unit tests are useful to quickly know if everything still works fine or something has been broken.
    That's important in plugin development (and any software development of course).


Log in to reply