SOLVED RenderDocument flags and return values?

Hello again;

while playing around with c4d.documents.RenderDocument, I found some strange behavior in certain flags, so I wonder whether the documentation is correct. Here's what I'm going with:

import c4d
from c4d import gui

def main():
    currentRenderData = doc.GetActiveRenderData()
    rdata = currentRenderData.GetData()
    imageWidth = int(currentRenderData[c4d.RDATA_XRES])
    imageHeight = int(currentRenderData[c4d.RDATA_YRES])
    bmp = c4d.bitmaps.MultipassBitmap(imageWidth, imageHeight, c4d.COLORMODE_RGB)
    bmp.AddChannel(True, True)

    result = c4d.documents.RenderDocument (doc, rdata, bmp,
                c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_OPEN_PICTUREVIEWER)
    if result == c4d.RENDERRESULT_OK :
        print ("Render ok")
        # c4d.bitmaps.ShowBitmap(bmp)
    elif result == c4d.RENDERRESULT_OUTOFMEMORY :
        print ("Error in rendering - Out of memory")
    elif result == c4d.RENDERRESULT_ASSETMISSING :
        print ("Error in rendering - Asset missing")
    elif result == c4d.RENDERRESULT_SAVINGFAILED :
        print ("Error in rendering - Failed to save")
    elif result == c4d.RENDERRESULT_USERBREAK :
        print ("Error in rendering - User break")
    elif result == c4d.RENDERRESULT_GICACHEMISSING :
        print ("Error in rendering - GI cache is missing")
    elif result == c4d.RENDERRESULT_NOMACHINE :
        print ("Error in rendering - Machine not found") # Only in Team Render
    elif result == c4d.RENDERRESULT_PROJECTNOTFOUND :
        print ("Error in rendering - Project was not found") # Only in command line render
    elif result == c4d.RENDERRESULT_ERRORLOADINGPROJECT :
        print ("Error in rendering - Error while loading project") # Only in command line render
    elif result == c4d.RENDERRESULT_NOOUTPUTSPECIFIED :
        print ("Error in rendering - Output was not specified") # Only in command line render

if __name__=='__main__':
    main()
  1. RENDERFLAGS_OPEN_PICTUREVIEWER does nothing. Explanation is "Open the Picture Viewer." but that doesn't happen. Nor does the finished image appear in the PV once I open it manually. (Note that I commented out ShowBitmap in the code above on purpose.)
  2. RENDERFLAGS_CREATE_PICTUREVIEWER does enter the image in an existing Picture Viewer with the correct name and time. If no PV is open, none is opened at this point even if the image will be shown if I open one manually. But the explanation is "Render in a new Picture Viewer." which I would interpret as "opening a new one"... maybe the explanation should be different.
  3. RENDERFLAGS_SHOWERRORS freezes my process - I have to kill C4D with the task manager. I'm not sure how to use that at all... does it require a parallel thread? I can't tell where the promised dialog would open...
  4. RENDERFLAGS_EXTERNAL is explained as "External render." (no comment...). In R19, the explanation was "Use render settings for LOD etc. (Otherwise the current document settings are used.)" which at least gives me some idea what this flag actually means. Was the text changed for a purpose? If yes, what does the flag do now?

As for the return values, I was unable to provoke a RENDERRESULT_ASSETMISSING. I created a material with an image texture and then renamed the file on disk. I get RENDERRESULT_OK anyway, with the material just missing in the final image. I tried the SHOWERRORS flag but the C4D process froze.

An empty file path in the render settings while having Save activated still doesn't cause a RENDERRESULT_SAVINGFAILED (naturally nothing is saved). Maybe it's only my expectation that it would? (Sorry, I was lazy and didn't test other file saving exceptions like Windows access rights...)

(btw in the current online documentation the result constants are mashed into one line, missing a proper Return, and maybe RENDERRESULT_OK should be listed too)

And finally, while the documentation contains a fine example for callbacks, I don't quite see how to interrupt the rendering to return RENDERRESULT_USERBREAK. I've been reading through quite a few historic posts on this forum regarding threading of RenderDocument, but by now I have the feeling that it's just not possible.

Hello @Cairyn,

thank you for reaching out to us. Regarding your points:

  1. RENDERFLAGS_OPEN_PICTUREVIEWER: I can confirm that, and I do not see anything that you are doing wrong.
  2. RENDERFLAGS_CREATE_PICTUREVIEWER: This is also only working sporadically for me.
  3. RENDERFLAGS_SHOWERRORS: I cannot confirm that. Using your script form above, Cinema does render fine for me on 23.110. On which version are you? Could you provide a script to invoke such freezing?
  4. RENDERFLAGS_EXTERNAL: Is working for me and does what its name implies, it allows for the usage of an external renderer. Could you also please provide here an example where it does not work for you? Here is a code snippet which gives you an idea how the flag is being used internally.
	if (!(renderflags & RENDERFLAGS::EXTERNAL) && (renderEngine == RDATA_RENDERENGINE_PREVIEWHARDWARE))
	{
		rdata.SetInt32(RDATA_RENDERENGINE, RDATA_RENDERENGINE_STANDARD);
		renderEngine = RDATA_RENDERENGINE_STANDARD;
	}

I.e., unless you pass RENDERFLAGS_EXTERNAL, you cannot render with the preview hardware renderer, even when your RenderData says so.

Regarding points one and two: They only show up in a minor capacity in the implementation of RenderDocument. There is some file path shuffling going on in the context of them, but even when I provided explicit file paths for the rendered image and document, the flags did not work for me in the way you (and I) would expect them to work.

I will ask the rendering team regarding the state of these two flags and then report back here.

Cheers,
Ferdinand

Hm,

minor addendum, I can get the flags RENDERFLAGS_OPEN_PICTUREVIEWER and RENDERFLAGS_CREATE_PICTUREVIEWER to work when I open all picture viewers. Even after closing them, both will work as intended. Probably have to dig here a bit more before I ask the devs.

Cheers,
Ferdinand

@ferdinand Thanks for confirming. About point #3 RENDERFLAGS_SHOWERRORS, the crash only happens if there actually are errors. If the render works without issues, it doesn't crash. The code is practically the same as above:

import c4d
from c4d import gui

def main():
    currentRenderData = doc.GetActiveRenderData()
    rdata = currentRenderData.GetData()
    imageWidth = int(currentRenderData[c4d.RDATA_XRES])
    imageHeight = int(currentRenderData[c4d.RDATA_YRES])
    bmp = c4d.bitmaps.MultipassBitmap(imageWidth, imageHeight, c4d.COLORMODE_RGB)
    bmp.AddChannel(True, True)

    result = c4d.documents.RenderDocument (doc, rdata, bmp,
                c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_SHOWERRORS)
    if result == c4d.RENDERRESULT_OK :
        print ("Render ok")
        c4d.bitmaps.ShowBitmap(bmp)
    elif result == c4d.RENDERRESULT_OUTOFMEMORY :
        print ("Error in rendering - Out of memory")
    elif result == c4d.RENDERRESULT_ASSETMISSING :
        print ("Error in rendering - Asset missing")
    elif result == c4d.RENDERRESULT_SAVINGFAILED :
        print ("Error in rendering - Failed to save")
    elif result == c4d.RENDERRESULT_USERBREAK :
        print ("Error in rendering - User break")
    elif result == c4d.RENDERRESULT_GICACHEMISSING :
        print ("Error in rendering - GI cache is missing")
    elif result == c4d.RENDERRESULT_NOMACHINE :
        print ("Error in rendering - Machine not found") # Only in Team Render
    elif result == c4d.RENDERRESULT_PROJECTNOTFOUND :
        print ("Error in rendering - Project was not found") # Only in command line render
    elif result == c4d.RENDERRESULT_ERRORLOADINGPROJECT :
        print ("Error in rendering - Error while loading project") # Only in command line render
    elif result == c4d.RENDERRESULT_NOOUTPUTSPECIFIED :
        print ("Error in rendering - Output was not specified") # Only in command line render

if __name__=='__main__':
    main()

To provoke an error, I have created a material with an image texture, assigned to a primitive. This works fine. Then I renamed the image file on disk. This still works fine (I suppose because the image is cached). Then I used "Reload Image" in the attribute manager for the material's image shader. Now the image doesn't show up in the material preview any more. Using the script now crashes the C4D instance.
Instead of "Reload image" you can also just restart C4D to clear the image cache. After restart, the same scene now doesn't find the image, and the script crashes.

Version is 23.110.

About #4: Maybe I wasn't completely clear, the flag does work (well, I don't see anything happening but at least it doesn't crash). I am just confused about the (somewhat redundant) explanation in the documentation.
So you say the flag is for external renderers (like Redshift, Cycles, Corona). At first reading, I thought so too, but I'm not sure about that any more. The R19 explanation is "Use render settings for LOD etc. (Otherwise the current document settings are used.)"
I am aware that LOD has a document setting and a render setting (there is a menu point "Use Render LOD for Editor Rendering" after all), so my interpretation of the flag would be "render outside the viewport", or "render to picture viewer" as the corresponding icon says.

Hmm. Maybe that's even what you mean... perhaps the deeper cause of my confusion is that the phrase "External Renderer" is not used in the GUI as far as I remember. So what is it:

  1. any renderer used to render to the picture viewer (not to the viewport)
  2. a renderer not built into C4D but added as plugin
  3. a renderer running in Team Render (batch render? command line render?)
  4. a renderer running on a Team Render client without GUI
  5. ....?
    Right now my money's on (1) in the context of RenderDocument.

Hello @Cairyn,

sorry for the long wait, this has not been forgotten by me, I will put some time into it tomorrow. I cannot gurantee that I will come to a conclusion right then, but it is the next thing on my desk.

Cheers,
Ferdinand

Hello @Cairyn,

here is the promised answer, please excuse the long wait. I have tested the flags now myself with R21, R23 and S24. All three versions behave identical and do the following for the listed flags:

RENDERFLAGS_OPEN_PICTUREVIEWER: Will not do anything. The rendering will not be added to the Picture Viewer (PC) and and the PC will not be opened.
RENDERFLAGS_CREATE_PICTUREVIEWER: PC will not be opened, but an image with the size (-1, -1) will be created with a memory/chip icon next to it in the PC. The image will not contain the results of the rendering.
RENDERFLAGS_SHOWERRORS: RenderDocument seems to be unable to handle AssetError/RENDERRESULT_ASSETMISSING and will freeze Cinema 4D in this case.

c4d.bitmaps.ShowBitmap(): Is working fine regarding opening an image in the PC.

At the bottom you can find a script version stating the same. I have created an issue/bug report for that behaviour.

Cheers,
Ferdinand

import c4d

RENDERRESULT_LOOKUP = {
    c4d.RENDERRESULT_OK: "RENDERRESULT_OK",
    c4d.RENDERRESULT_OUTOFMEMORY: "RENDERRESULT_OUTOFMEMORY",
    c4d.RENDERRESULT_ASSETMISSING: "RENDERRESULT_ASSETMISSING",
    c4d.RENDERRESULT_SAVINGFAILED: "RENDERRESULT_SAVINGFAILED",
    c4d.RENDERRESULT_USERBREAK: "RENDERRESULT_USERBREAK",
    c4d.RENDERRESULT_GICACHEMISSING: "RENDERRESULT_GICACHEMISSING",
    c4d.RENDERRESULT_NOMACHINE: "RENDERRESULT_NOMACHINE",
    c4d.RENDERRESULT_PROJECTNOTFOUND: "RENDERRESULT_PROJECTNOTFOUND",
    c4d.RENDERRESULT_ERRORLOADINGPROJECT: "RENDERRESULT_ERRORLOADINGPROJECT",
    c4d.RENDERRESULT_NOOUTPUTSPECIFIED: "RENDERRESULT_NOOUTPUTSPECIFIED",
}


def main():
    """
    """
    # The active render data container.
    rd = doc.GetActiveRenderData()
    bc = rd.GetData()

    # The render size, we are looking at it because for 
    # RENDERFLAGS_CREATE_PICTUREVIEWER, Cinema 4D, the Picture Viewer, will
    # create an image with the size of (-1, -1), at least that is what is 
    # being displayed in the Picture Viewer.
    width = int(rd[c4d.RDATA_XRES])
    height = int(rd[c4d.RDATA_YRES])
    print ("render resolution:", width, height)

    # The bitmap to render into.
    bmp = c4d.bitmaps.MultipassBitmap(width, height, c4d.COLORMODE_RGB)
    bmp.AddChannel(True, True)

    # Invoke c4d.documents.RenderDocument with some flags, I have tested all
    # flags with R21, R23 and S24 and they all behave identically in these
    # versions. 
    RenderDocument = lambda f: c4d.documents.RenderDocument(doc, bc, bmp, f)

    # Will literally not do anything ...
    res = RenderDocument(c4d.RENDERFLAGS_OPEN_PICTUREVIEWER)
    print("RENDERFLAGS_OPEN_PICTUREVIEWER:", RENDERRESULT_LOOKUP[res])

    # Will not open the picture viewer, but create an "image" with the size
    # (-1, -1) and a chip/memory icon as the preview.
    
    # res = RenderDocument(c4d.RENDERFLAGS_CREATE_PICTUREVIEWER)
    # print("RENDERFLAGS_OPEN_PICTUREVIEWER:", RENDERRESULT_LOOKUP[res])

    # Will freeze Cinema 4D when an Asset Error is being raised, i.e., when
    # res should be `RENDERRESULT_ASSETMISSING`. To invoke an Asset Error,
    # simply rename or delete the asset and force Cinema to rebuild the 
    # caches for the scene by for example reloading the scene.
    
    # res = RenderDocument(c4d.RENDERFLAGS_SHOWERRORS)
    # print("RENDERFLAGS_SHOWERRORS:", RENDERRESULT_LOOKUP[res])

    # Will properly open the rendered image in the Picture Viewer.
    c4d.bitmaps.ShowBitmap(bmp)

if __name__ == '__main__':
    main()

Hello again @Cairyn 😉 ,

I did deliberately separate your question(s) regarding RENDERFLAGS_EXTERNAL from my previous posting, due to here working everything as it should (at least from my point of view). Rather than giving a wordy explanation, I wrote a piece of narrative code, which hopefully does clear up any uncertainties.

Cheers,
Ferdinand

import c4d


def main():
    """
    """
    # These are the built-in render engine symbols, when the render data
    # parameter RDATA_RENDERENGINE != RDATA_RENDERENGINE_STANDARD holds true,
    # the rendering is considered to be an external rendering. This could of
    # course be something like Arnold, Corona, etc., but also holds true for
    # the hardware preview renderer (which admittedly is a bit of a stretch
    # to be called "external").

    # RDATA_RENDERENGINE                 = 5300,
    # RDATA_RENDERENGINE_STANDARD        = 0,
    # RDATA_RENDERENGINE_PREVIEWHARDWARE = 300001061,
    # RDATA_RENDERENGINE_PHYSICAL        = 1023342,
    # RDATA_RENDERENGINE_CINEMAN         = 1016630,
    # RDATA_RENDERENGINE_GPURENDERER     = 1037639,
    # RDATA_RENDERENGINE_CPURENDERER     = 1041348,

    # The active render data container.
    rd = doc.GetActiveRenderData()
    bc = rd.GetData()
    print(bc[c4d.RDATA_RENDERENGINE])

    # We manually set the render engine to the hardware preview renderer.
    bc[c4d.RDATA_RENDERENGINE] = c4d.RDATA_RENDERENGINE_PREVIEWHARDWARE

    # Create an image for the rendering, matching our render data.
    width = int(rd[c4d.RDATA_XRES])
    height = int(rd[c4d.RDATA_YRES])
    bmp = c4d.bitmaps.MultipassBitmap(width, height, c4d.COLORMODE_RGB)
    bmp.AddChannel(True, True)

    # Render the image: Cinema will ignore the render engine defined in our
    # render data, because we did not specify that we want to allow for
    # external render engines. Cinema will use instead RENDERENGINE_STANDARD.
    res = c4d.documents.RenderDocument(doc, bc, bmp, c4d.RENDERFLAGS_NONE)
    c4d.bitmaps.ShowBitmap(bmp)

    # Create an image for the rendering, matching our render data.
    width = int(rd[c4d.RDATA_XRES])
    height = int(rd[c4d.RDATA_YRES])
    bmp = c4d.bitmaps.MultipassBitmap(width, height, c4d.COLORMODE_RGB)
    bmp.AddChannel(True, True)

    # This time Cinema will use the RENDERENGINE_PREVIEWHARDWARE, due to us
    # passing the required flag.
    res = c4d.documents.RenderDocument(doc, bc, bmp, c4d.RENDERFLAGS_EXTERNAL)
    c4d.bitmaps.ShowBitmap(bmp)


if __name__ == '__main__':
    main()

Hello @Cairyn,

without any further questions or replies, we will consider this topic as solved by Monday and flag it accordingly.

Thank you for your understanding,
Ferdinand

@ferdinand oops, I kinda forgot to close this. Thanks for looking it up, I guess regarding the RENDERFLAGS error display I will need to wait for a correction. The RENDERFLAGS_EXTERNAL is a bit of a surprise, I would not have interpreted it like this, but it's fine, I just set it every time.

Thanks again!

Hey @Cairyn,

thank you for closing it.

FYI: When you scroll up to your first post, you will see that it has the tag [fixed issues]. This is a recently introduced tag with which we track things we intend to fix. So, although the topic has been closed, we have it still in a list of things that are relevant for us, the issues we have to fix. When we have applied the fix, we will answer in this thread to announce that and then also remove the [fixed issue] tag. This tag does not necessarily mean that this will be a software fix but could also mean that this will be a documentation fix.

Just as explanation for you and everyone else reading for why we are pushing for closing things. If we do not do this, we will end up with an ever-growing list of unsolved topics. But we understand the necessity and desire for tracking things beyond that. Which is why we introduced that tag.

Cheers,
ferdinand