Group Details Private

Global Moderators

Forum wide moderators

  • RE: how to get vertex color tag's data for a specific frame (different with current frame) without modifying current document?

    Hey @BruceC,

    uff, that is a lot of points. I'll try to comb through most of it, but I do not have that much time today. Feel free to ask for clarifications where I left out things.

    Currently, I do this in the tag plugin's Execute() function. The tag plugin remembers the vertex color tag's pointer (initialized as nullptr) and dirty checksum (by C4DAtom::GetDirty(), and initialized as 0).

    Never user pointers to long term keep track of scene elements. Something not being nullptr does not mean you point to valid data. The node you point to could be long deleted, and then you point to garbage and access attempts will then result in access violations and a crash. Either use a BaseLink to store a reference to a scene element, or use a weak pointer. I.e., a pointer which becomes invalid when the pointed data is being deleted. You can for example see here how to use a maxon::WeakRawPtr.

    However, I found ExecutePasses() called in PrerollToTime() triggers the tag plugin's Execute() function (I think this is the cloned tag in the cloned document), and Execute() function tries to read the linked vertex color tag at the speficied frame again, so PrerollToTime() -> ExecutePasses() is called again, and triggers a new cloned tag in a new cloned document to run its Execute(), and so on.

    Yes, that is true and a common problem (not only in Cinema 4D). The superficial reason is that my preroll code example also passes True for the third argument expressions (effectively API slang for tags). When you would pass there false, your tag would not be executed again. But also all other tags would not be executed (which is likely not what you want). The solution to this can be registering a plugin ID, e.g., ID_FOO_PLUGIN_IS_COMPUTE_PASS. When you then clone your document, or execute the passes on an existing document, you write under that ID for example a bool into the data container of the document. Your tags Execute(tag, ...) then gets the document from tag and checks for that flag being present, to stop what it does that causes the infinite update loop. When operating on a document not owned by you, you would then delete that flag or set it to false after you manually invoked the passes, so that future updates made by Cinema 4D (which owns and operates the document) then take the 'normal' route.

    The better solution is to generally design things in a manner that such infinite update loops cannot happen in the first place. I do not understand your problem well enough to give more concrete advice here. These scenarios are also not always avoidable.

    According to the sample code PrerollToTime(), the lastFrame (the target frame) must not be earlier than the current scene's time.

    The preroll function was just an example, not something you must follow to the letter. There is also a little bug I just see now, it should be of course if (lastFrame < firstFrame - 1). But the general idea is, that when you have a scene with simulations, i.e., stuff like Pyro or particles, where the state of the current frame depends on the last frame, you must basically cycle through all the correct scene states to get to that state. You can of course also jump to a previous point, but then you do not have to sim from NOW to FUTURE but from 0 to PREVIOUS.

    Prerolling is not necessary when the scene does not contain any simulations, e.g., just key framed position animations, all simulations are cached, or you do not care about simulations and their impact on the rest of scene. Given what kind of plugins you do develop, you likely want to preroll.

    ⚠ Just to stress this again: Prerolling can be extremely expensive. When the user has a scene with 300 frames of Pyro simulations worth 30 minutes of computing, and you try to preroll half of it, your preroll function will run for 15 minutes.

    What to do here, really depends on the exact case. Generally you try to avoid having to reach into the future or the past, as this is often a big no-no. The most common "pro" solution to that is caching. E.g., that you write yourself a vertex color cache in your case. When you only need to reach into the past, and this only applies in rendering, you could also effectively hide this from the user, as you must have passed and naturally calculated that value before (and therefore need no explicit cache recording).

    Will answer the (2) portion on Monday, running a bit out of Friday time right now.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to receive the high definition / Interpolation version of a spline?

    Hey @mogh,

    that could be that I was wrong. But it was at least not an unintentional typo. That is how I remembered it and what is also usually the case in Cinema 4D. v3 is an alias for the k component of an i, j, k frame, i.e., the "z-axis". This is usually considered to be "the normal", i.e., when one constructs a frame, one makes it so that v3/k becomes the normal of what one is constructing that frame for. And to be verbose here, frame (of reference) is just some math slang for a coordinate system, i.e., what on a more general level is also called a "transform", or even more generic and how Cinema 4D handles it, a "matrix".

    Bottom line is here, when v1 works for you as a normal, just use it, there is nothing which inherently makes k/v3 "the normal" of a frame. Maxime also touched spline transport some time ago, it could be that he switched things around.

    There is only the Matrix Manual in Python, in C++ is nothing at all. Users are just supposed "to know" such stuff. I never find the time to do more than what I once did there. And just as warning, the normals of a spline are usually not what non-math people expect them to be, due to the fact that they are defined by curvature and that the concept of inflection points does exist. We once talked here about this, including an illustration.

    That is why in CGI the normals of a spline are usually not the normals, but the parallel transport of some normal plus some upvector which determines the initial orientation. E.g., SplineHelp realizes parallel transport, so that you can for example have an "outline" of a spline which makes sense for artists. But as you might have noticed in the past as a user, outlining a spline does not always produce results a human would consider correct. Just search for "parallel tranport" here on the forum and limit the results to my user, we talked many times about it, including code examples etc.

    So, long story short again, there is no mathematical way to compute the "perfect" normals for a spline, at least what humans consider this to be. And depending on the spline and the fanciness with which you implement parallel transport, you might be subject to normal banking and/or flipping issues. So, SplineHelp tries to give you normals/frames that humans would consider correct, but the quality of its efforts might vary.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: how to get vertex color tag's data for a specific frame (different with current frame) without modifying current document?

    Hey @BruceC,

    Thank you for reaching out to us. Let's split these effectively two questions into two parts.

    Evaluating a BaseDocument at time #t

    When you want to know the state of a scene at a time, #t, you can do this by setting the time of the document (BaseDocument::SetTime) and then executing its passes (BaseDocument::ExecutePasses), which is CInema 4D API slang for updating the scene state. This is configurable and can evaluate animations, build caches, and execute expressions, i.e., do the whole scene evaluation. But when you have a very heavy document, executing the passes is of course a not so cheap operation.

    A more clever solution can be to manually interpolate animations - when that is all you need - via CCurve.GetValue. But that only works for animations that defined by keyframes and are decomposable into float values. The former is here not given for vertex colors, as you can only animate them procedurally.

    This is all further complicated in a real life scenario, as being at frame t, then jumping to frame t + 10, and then just executing the passes might not be enough. The reason is because what you to query might be dependent on simulations; and your output will then be wrong when you just execute frame t and t + 10, instead of t, t + 1, ..., t + 10.

    So, a preroll function could look like this:

    using namespace cinema;
    
    static maxon::Result<void> PrerollToTime(BaseDocument* const doc, const BaseTime& time)
    {
      iferr_scope;
      CheckArgument(doc, "doc"_s, "Document is nullptr."_s);
    
      const Int32 fps = doc->GetFps();
      const Int32 firstFrame = doc->GetTime().GetFrame(fps) + 1;
      const Int32 lastFrame = time.GetFrame(fps);
      if (lastFrame < firstFrame - 1)
        return maxon::IllegalArgumentError(
          MAXON_SOURCE_LOCATION, "The target time is before the current time."_s);
    
      // Loop over all frames we need to reach #t and execute the passes. What to execute for each pass
      // (caches, animations, expressions) depends on what you want to achieve, and will of course have
      // an impact on the performance. Note that things likes cache building can have an impact on
      // animations and expressions, and vice versa. So, when one needs the 'true' scene state, one must
      // often calculate everything.
      for (Int32 frame = firstFrame; frame <= lastFrame; ++frame)
      {
        doc->SetTime(BaseTime(frame, fps));
        // Execute the passes for everything in the main thread (nullptr), use your thread instead when
        // you have one.
        if (!doc->ExecutePasses(nullptr, true, true, true, BUILDFLAGS::NONE))
          return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Failed to execute pass."_s);
      }
      
      // For the last frame we should execute the passes twice, so that the scene can settle.
      if (!doc->ExecutePasses(nullptr, true, true, true, BUILDFLAGS::NONE))
          return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Failed to execute pass."_s);
    
      // The document is now in the state of #time.
      return maxon::OK;
    }
    

    Finding a scene element #e in two scenes

    I am a bit surprised that you ask that, as this is sort of the bread and butter for what you are doing. So, I might be misunderstanding you here. If that is so, please clarify what you mean.

    The identity of scene elements is expressed in Cinema 4D by GeMarker. Such markers are given when objects are allocated (and by default also when copied) and are a hash of the creating machine's mac address, a timestamp and some secret sauce. An alternative way to retrieve the same data is BaseList2D::FindUniqueID(MAXON_CREATOR_ID) which just returns the raw data of the GeMarker obtainable via BaseList2D::GetMarker. When you copy scene elements or whole scenes, by default they are given new markers. You can prevent that with COPYFLAGS::PRIVATE_IDENTMARKER.

    ⚠ Never insert multiple nodes with the same marker into a scene.

    An alternative could be to use links, i.e., a BaseLinks, for example in a BaseContainer stored with the document. But that is also just built on top of GeMarker.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to handle AddDependence hh deprecation in 2024 onwards

    Hey @kbar,

    I hope you are well! Since I just answered a similar question in another topic, I am also answering here. We never make any guarantees regarding when something will be released in SDKs. And more-over, we never name upcoming Cinema 4D versions explicitly or when they will arrive. This also includes seemingly insignificant information such as if the next version will be a minor or a major version increment.

    Please note that you as Maxon Registered Developer are bound by an NDA and also cannot disclose such information to the public.

    But yes, I am pretty sure Maxime meant the next upcoming release.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: Picture Viewer does not display correct colors for RenderDocument bitmap

    Hello @patrick_cue,

    Welcome to the Maxon developers forum and its community, it is great to have you with us!

    Getting Started

    Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

    • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
    • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
    • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

    It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.

    About your First Question

    Please do not use other topics for your support requests, especially when they are bug tickets. Asking a follow-up questions such as "when will this be fixed" or "does this also apply to situation B" in a foreign topic is allowed and even encouraged.

    But a specific case with your specific code and data always warrants a new thread. Please also familiarize yourself with Support Procedures: Asking Questions, as while it its clear that you put effort into make yourself understood (thanks!), this is also bordering a bit on the too much information.

    The bug tracked by the other topic, is that there is currently no way in the Python API to use c4d.documents.RenderDocument in an OCIO document (i.e., every document since 2025.0) and then display that bitmap with the correct colors via c4d.bitmaps.ShowBitmap. It is important to understand that:

    The bitmap data generated is just fine, when you save the bitmap to disk, everything is correct. It is just that the Picture Viewer does not display it incorrectly.

    The reason for that is that in Python OCIO is not fully exposed in 2025.1.0 and you cannot set the OCIO profiles of the rendered bitmap there. Internally, we have been working on an OCIO port for Python allowing for color conversions and more things, including OCIO managing bitmaps. We also added a function to bake a display and view transform into an sRGB image - another route how this RenderDocument issue can be solved.

    The goal is to publish this with an upcoming version of the Python SDK, ideally the next, but as always we cannot make any guarantees. You cannot fix this issue yourself in 2025.1.0 and lower.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: Retrieve. c4d from folders and subfolders and update tree list

    Hi @Neekoe,

    That's great to hear you've managed to solve the issue!

    For your future postings please note that according to our Support Procedures:

    We cannot debug your code for you and instead provide answers to specific problems.

    This effectively means that you're welcome to ask your questions and attach the code snippet that demonstrates your question or issue you're struggling with. However, we cannot debug your entire codebase, so you have to isolate your specific question and the corresponding part of code yourself before asking the question.

    Cheers,
    Ilia

    posted in Cinema 4D SDK
  • RE: Image Viewer API

    Hi @kmhfygg,

    There's no such thing as "Image Viewer".

    If you mean rendered images that are available in the history list of the "Picture Viewer", @Dunhou
    is correct - there's no public API that allows you to access them.

    If instead you'd like to access the output frame settings in the "Render settings" (i.e. "Frame Range", "From", "To", "Frame Step", etc..), please have a look at related thread: Access renderer specific settings with python.

    Cheers,
    Ilia

    posted in Cinema 4D SDK
  • RE: How to receive the high definition / Interpolation version of a spline?

    @mogh said in How to receive the high definition / Interpolation version of a spline?:

    from the above helper I only get the points the spline is set to (eg. adaptive 5°) but I perhaps want more points in between.

    That is not correct. As I said, splines have theoretically infinite precision, you are only bound in practice by the floating point precision you use for your computations - double, i.e., 64 bit in Cinema 4D. You get in your code just the cache of your spline and iterate over its points. That is more or less the same as if you would have just called BaseObject.GetCache() on your SplineObject which I mentioned above.

    When you want to interpolate your spline manually, you must use the interpolation methods of SplineHelp, e.g., GetMatrix, which is sort of the most potent of all, as it gives you a full frame for each sample (i.e., not just a point but also an orientation).

    I have a hunch that the actual question is how to build the cache of a spline as if it would have higher interpolation settings. And for that you should copy your input (unless it is already a dangling, i.e., non-inserted node), change its settings, insert it into a dummy document, and then call ExecutePasses on that document. Then you can get the cache, and iterate over its points.

    Cheers,
    Ferdinand

    f91a1f2b-2eab-44ee-a689-a981e9d545f8-image.png

    import c4d
    
    doc: c4d.documents.BaseDocument  # The currently active document.
    op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
    
    def main() -> None:
        """Called by Cinema 4D when the script is being executed.
        """
        if not op:
            return
        
        helper = c4d.utils.SplineHelp()
        if not helper.InitSplineWith(op, c4d.SPLINEHELPFLAGS_RETAINLINEOBJECT):
            raise RuntimeError("Could not initialize spline helper.")
        
        # Take exactly ten samples over the length of the spline, no matter how many vertices it has,
        # and how many points its current LineObject cache has.
        steps: int = 10
        for i in range(steps + 1):
            # Get a frame for the offset #t.
            t: float = i/float(steps)
            m: c4d.Matrix = helper.GetMatrix(t)
    
            # Print the #t, the point #p on the spline, its normal #n, tangent #t, and bi-tangent #bt.
            print(f"{t = }, p = {m.off}, n = {m.v3}, t = {m.v1}, bt = {m.v2}")
    
    if __name__ == '__main__':
        main()
    
    posted in Cinema 4D SDK
  • RE: TagData Plugin with Gui not using *.res

    Hey @mogh,

    that is not possible, nodes, i.e., NodeData, e.g., TagData, always need a description (and with that res file).

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK
  • RE: How to receive the high definition / Interpolation version of a spline?

    Hey @mogh,

    Thank you for reaching out to us. What do you mean with "low" detail? Something like the sampling angle of a spline in "adaptive" mode? The interpolation settings of a spline only apply when you quantize it, i.e., the degree of precision with which a SplineObject is being quantized into its LineObject cache (i.e., what is returned for BaseObject.GetCache() for a spline).

    Splines themselves are just series of polynomials and therefore have no inherent precision. So, when you call for example SplineHelp.GetMatrix() to get all the metadata for an offset, you are not subject to the interpolation settings. That is within the limits of what computers can do regarding arithmetic of irrational numbers. You can quite quickly get subject to floating point precision problems when working with splines. If they are fixable and how to fix them, depends on the concrete case.

    Cheers,
    Ferdinand

    posted in Cinema 4D SDK