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  😉

    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


Log in to reply