Cinema Freezes while rendering

On 14/03/2013 at 10:00, xxxxxxxx wrote:

Hey everyone,

Sorry for bothering you all so soon again, but i have some code in a python plugin, that starts several renders, and while it renders, cinema freezes. It doesn't become unresponsive per say, i just can't do anything until the rendering of the sequence is finished. If you have a scene rendering for like 15 mins or a sequence/animation, you have to wait forever. Is there a way to keep that from happening?

My code looks like this:

  
  
    
    
     def renderDocument(self, layerName) :  
          """  
          Taking the document and rendering it according to the normal rendersettings of the document.  
          Ads '_LAYERNAME', LAYERNAME being the name of the layer, to the renderpath.  
          """  
          doc = c4d.documents.GetActiveDocument()  
          renderData = doc.GetActiveRenderData()  
          xResolution = int(round(renderData[c4d.RDATA_XRES]))  
          yResolution = int(round(renderData[c4d.RDATA_YRES]))  
          pathChange = False  
      
          if renderData[c4d.RDATA_PATH] != "":  
              storedPath = renderData[c4d.RDATA_PATH]  
              renderData[c4d.RDATA_PATH] =r'{0}_{1}'.format(renderData[c4d.RDATA_PATH], layerName)  
              c4d.EventAdd()  
              pathChange = True  
      
          renderBmp = c4d.bitmaps.BaseBitmap()  
          renderBmp.Init(x=xResolution, y=yResolution, depth=32, )  
      
          result = documents.RenderDocument(doc, renderData.GetData(), renderBmp, c4d.RENDERFLAGS_EXTERNAL)  
          if pathChange == True:  
              renderData[c4d.RDATA_PATH] = storedPath  
          if result==c4d.RENDERRESULT_OK:  
              c4d.bitmaps.ShowBitmap(renderBmp)     # show image in pictureViewer  
          return True
  
  

How could i tackle that?
Or is there even a way to create custom layers (like for example ao pass, just with custom content?), so that i could render everxthing at once?

Thanks in advance

Aurel

On 14/03/2013 at 10:22, xxxxxxxx wrote:

You can execute your code in a threaded environment. Just Google for threads in Python. There's
a built-in threading facility in Python, but in this case it makes much sense to use Cinema 4D's
threading interface, since you can pass the thread-object to the RenderDocument() function and it
will allow you to interrupt rendering (when I understood it correctly).

See the c4d.threading.C4DThread class.

I can show you an example when I find the time.

Best,
-Niklas

On 15/03/2013 at 00:36, xxxxxxxx wrote:

I will google that, an example would be much appreciated though!

Aurel

On 15/03/2013 at 00:52, xxxxxxxx wrote:

To clarify:

I don't want to interrupt the render, i just want to let it render (preferably in the picture viewer as usual) and work on the scene at the same time. So that i can see the image as it's being rendered, so that a folder appears for an image sequence in the pictureViewer, and all the images appear in it.

Thanks
Aurel

On 15/03/2013 at 02:02, xxxxxxxx wrote:

I read the sdk docs on threading, and i did the following:

I pasted the 'rendercode' into the main method of the C4DThread class:

  
  
class RenderThread(C4DThread) :  
  def Main(self) :  
      doc = c4d.documents.GetActiveDocument()  
      renderData = doc.GetActiveRenderData()  
      xResolution = int(round(renderData[c4d.RDATA_XRES]))  
      yResolution = int(round(renderData[c4d.RDATA_YRES]))  
      pathChange = False  
  
      if renderData[c4d.RDATA_PATH] != "":  
          storedPath = renderData[c4d.RDATA_PATH]  
          renderData[c4d.RDATA_PATH] =r'{0}_{1}'.format(renderData[c4d.RDATA_PATH], layerName)  
          c4d.EventAdd()  
          pathChange = True  
  
      renderBmp = c4d.bitmaps.BaseBitmap()  
      renderBmp.Init(x=xResolution, y=yResolution, depth=32, )  
  
      result = documents.RenderDocument(doc, renderData.GetData(), renderBmp, c4d.RENDERFLAGS_EXTERNAL)  
      if pathChange == True:  
          renderData[c4d.RDATA_PATH] = storedPath  
      if result==c4d.RENDERRESULT_OK:  
          c4d.bitmaps.ShowBitmap(renderBmp)     # show image in pictureViewer  
      return True  
  

then, in the renderDocument Method, i do the following:

  
  
  def renderDocument(self, layerName) :  
      """  
      Taking the document and rendering it according to the normal rendersettings of the document.  
      Ads '_LAYERNAME', LAYERNAME being the name of the layer, to the renderpath.  
      """  
      renderThread = RenderThread()  
      renderThread.Start()  
      return True  
  

All i get from that is 3 TypeErrors:

TypeError: thread object is dead
TypeError: thread object is dead
TypeError: thread object is dead

And thats it.

I am btw. aware of the fact that i should pass in layerName into the Main method, but Main() is always being called with only one argument, so don't know what to do about that. But that's for later, when the thread object doesn't just die on me all the time anymore  :wink:

Help is always much appreciated,

Aurel

On 15/03/2013 at 05:52, xxxxxxxx wrote:

Hi Amadeo,

you must keep a reference to the thread, it will be garbage collected otherwise.

thread = RenderThread()
thread.Start()
  
# ... do some stuff
  
# wait for the thread to finish.
thread.Wait(False)

This is why threading is not (easily) done in a Script. When using a dialog or command-data plugin,
you can store the script in your plugin sub-class.

class Command(c4d.plugins.CommandData) :
  
    thread = None
  
    def Execute(self, doc) :
        if self.thread and not self.thread.IsRunning() :
            # Thread is already/still running, what do you want to do
            # in this case?
            pass
        else:
            self.thread = RenderThread()
  
        return True

For the layerName: You can pass it on construction.

class RenderThread(c4d.threading.C4DThread) :
  
    def __init__(self, layerName) :
        super(RenderThread, self).__init__()
        self.layerName = layerName
  
    def Main(self) :
        # ...
        pass
  
# ...
  
thread = RenderThread(layerName)

Best,
-Niklas

On 15/03/2013 at 08:58, xxxxxxxx wrote:

Hey NiklasR,

Thanks for the examples! I'm not really sure about the second example:

  
    
    
    class Command(c4d.plugins.CommandData) :
    
      
    
    
        thread = None
    
      
    
    
        def Execute(self, doc) :
    
            if self.thread and not self.thread.IsRunning() :
    
                # Thread is already/still running, what do you want to do
    
                # in this case?
    
                pass
    
            else:
    
                self.thread = RenderThread()
    
      
    
    
            return True

Especially since i have an ObjectData Plugin - is that a problem?

I was able to get the threading working, but it doesn't really make a difference, cinema still freezes until the renders are done - I set it up like this:

  
class RenderThread(C4DThread) :  
  
  def __init__(self, layerName) :  
      super(RenderThread, self).__init__()  
      self.layerName = layerName  
  
  def Main(self) :  
      doc = c4d.documents.GetActiveDocument()  
      renderData = doc.GetActiveRenderData()  
      xResolution = int(round(renderData[c4d.RDATA_XRES]))  
      yResolution = int(round(renderData[c4d.RDATA_YRES]))  
      pathChange = False  
  
      if renderData[c4d.RDATA_PATH] != "":  
          storedPath = renderData[c4d.RDATA_PATH]  
          renderData[c4d.RDATA_PATH] =r'{0}_{1}'.format(renderData[c4d.RDATA_PATH], self.layerName)  
          c4d.EventAdd()  
          pathChange = True  
  
      renderBmp = c4d.bitmaps.BaseBitmap()  
      renderBmp.Init(x=xResolution, y=yResolution, depth=32, )  
  
      result = documents.RenderDocument(doc, renderData.GetData(), renderBmp, c4d.RENDERFLAGS_EXTERNAL)  
      if pathChange == True:  
          renderData[c4d.RDATA_PATH] = storedPath  
      # if result==c4d.RENDERRESULT_OK:  
      #     c4d.bitmaps.ShowBitmap(renderBmp)     # show image in pictureViewer  

And then call it like this:

  
  def renderDocument(self, layerName) :  
      """  
      Taking the document and rendering it according to the normal rendersettings of the document.  
      Ads '_LAYERNAME', LAYERNAME being the name of the layer, to the renderpath.  
      """  
      renderThread = RenderThread(layerName)  
      renderThread.Start()  
      renderThread.Wait(False)  
      return True  

But cinema only becomes responsive again once the plugin is finished with all renders (makes sense in a way.)

One idea i have:

Is it possible to render the things exactly like cinema does usualy? meaning:

- save a copy of the scene somwhere temporarely, render it this way and therefor not influencing the scene in any way? and that in the picture viewer it updates all the images as they are being rendered with tiles and everything?

Thank you!

Aurel

On 15/03/2013 at 09:01, xxxxxxxx wrote:

Hi Amadeo,

it shouldn't be a problem, you just have to store the thread object somewhere until it is finished
(eg in your plugin subclass instance, like in the CommandData example).

Read the documentation to C4DThread.wait(). It tells you that the function will wait until the thread
is done, and this of course blocks the main loop of Cinema (causing the freeze). Just don't call
Wait() on the thread. In the example above, I have done it since it was necessary to keep the
script running until the thread was finished, and this was achieved by calling Wait() which does
exactly this.

Is it possible to render the things exactly like cinema does usualy?

I don't think so, could be wrong though.

-Niklas