Questions about Bake Texture tag & Python



  • Hello,

    I have some questions concerning the Bake Texture tag:

    1 - Is there command that allow to open the bake preview window

    bake_texture_tag_open_win.jpg

    2 - Is it possible to detect when the bake render is finished

    I imagine that there is no way to display the small image preview inside description resource via python.

    Thank you.



  • Hi,

    1. Not really in Python, since it is inside a context menu of an gadget within a description resource.
    2. You might want to check c4d.CheckIsRunning(c4d.CHECKISRUNNING_BAKING), but I am not sure if it does work for all scenarios of baking.

    Cheers
    zipit



  • @zipit said in Questions about Bake Texture tag & Python:

    c4d.CheckIsRunning(c4d.CHECKISRUNNING_BAKING)

    Hi,
    I tried with c4d.CheckIsRunning(c4d.CHECKISRUNNING_BAKING) but this always returning false.



  • @zipit

    Hi,
    Since the c4d.CheckIsRunning(c4d.CHECKISRUNNING_BAKING) doesn't work, I'm trying to use an alternative method to check if baking is running.

    The problem is that the print() result work but the static text field (description resource) of my object dosen't change.

    Also the GetDEnabling() don't react to my custom CheckIsRunning condition or the CheckIsRunning condition doesn't send the change.

    see code below:

    def Init(self, op):
        self.PyCObject_id = None
        self.hide_bake_button = None
        self.hide_stop_bake_button = c4d.MYOBJECT_BAKETEXTURE_STOP
    
    def Message(self, node, type, data):
            
        if type == c4d.MSG_DESCRIPTION_COMMAND: 
            id = data['id'][0].id
              
            # My custom bake texture button   
            if id == c4d.MYOBJECT_BAKETEXTURE_BAKE:
                self.hide_bake_button = c4d.MYOBJECT_BAKETEXTURE_BAKE
    
                # Bake texture function
                self.BakeTexture(node, c4d.BAKETEXTURE_BAKE)
    
            # My custom stop bake texture button 
            if id == c4d.MYOBJECT_BAKETEXTURE_STOP:
                self.hide_stop_bake_button = c4d.MYOBJECT_BAKETEXTURE_STOP
                self.hide_bake_button = None
    
                # Bake texture function
                self.BakeTexture(node, c4d.BAKETEXTURE_STOP)
    
        # Check if bake texture is running (my custom CheckIsRunning condition)
        if type == 14:  
            if data['type'] == 1001071:
                pydata= data['data']            
                pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
                pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
                PyCObject_id = pythonapi.PyCObject_AsVoidPtr(pydata)
                
                if self.PyCObject_id != PyCObject_id:
                    self.PyCObject_id = PyCObject_id
                    print("Bake texture is running")
                    
                    # issue with the following line
                    node[c4d.MYOBJECT_BAKETEXTURE_STATICTEXT_STATE] = "Bake texture is running" # STATICTEXT description resource
                    
                else:
                    print("Bake texture is finished")
    
                    # issue with the following lines 
                    self.hide_bake_button = None
                    node[c4d.MYOBJECT_BAKETEXTURE_STATICTEXT_STATE] = "Bake texture is running" # STATICTEXT description resource
    
    
    def BakeTexture(self, op, event):
            obj = op.GetDown()
    
            tag = obj.GetTag(c4d.Tbaketexture)
    
            tag[c4d.BAKETEXTURE_FORMAT] = c4d.FILTER_HDR        
            tag[c4d.BAKETEXTURE_CHANNEL_REFLECTION] = True
    
            c4d.CallButton(tag, event)
    
            return tag
    
    def GetDEnabling(self, node, id, t_data, flags, itemdesc):
            
        if id[0].id == self.hide_bake_button:
            return False
    
        if id[0].id == self.hide_stop_bake_button:
            return False
    
        return True
    


  • Hi,

    you can't assign a value to a STATICTEXT description element as it is a static element as the name implies ;) You have either to use the STRING element (here you can just assign a value) or overwrite NodeData.GetDDescription() to modify your description (of your STATICTEXT) dynamically.

    Cannot say much about your GetDEnabling, since you are only checking some DescIDs there. I don't really get what is not working there?

    Cheers
    zipit



  • @zipit
    I tried with all types (float, string, bool..) and that not works, nothing happens.

    And about the GetDEnabling
    for example in the condition :

    if id == c4d.MYOBJECT_BAKETEXTURE_BAKE:
          self.hide_bake_button = c4d.MYOBJECT_BAKETEXTURE_BAKE
    

    the GetDEnabling react and disable the bake button.

    but in CheckIsRunning condition the GetDEnabling don't react and re-enable my custom bake:

    if type == 14:  
        if data['type'] == 1001071:
            ...
            if self.PyCObject_id != PyCObject_id:
                self.PyCObject_id = PyCObject_id
                ...
            else:
                self.hide_bake_button = None # <<< here the bake button is not re-enabled
    

    it is a bit complicated to explain the issue, if this is not clear I will try to create a full example and share it here.
    sorry, and thank you for your time.



  • Problem solved by adding a global variable.

    # Global variable
    checkisrunning_baking = False
       
    class MyClass(c4d.plugins.ObjectData):
    
        def Init(self, op):
            self.PyCObject_id = None
            self.hide_bake_button = None
            self.hide_stop_bake_button = c4d.HDRISG_BAKETEXTURE_STOP
    
        def Message(self, node, type, data):
            global checkisrunning_baking
    
            if type == c4d.MSG_DESCRIPTION_COMMAND: 
                id = data['id'][0].id
              
                # My custom bake texture button   
                if id == c4d.MYOBJECT_BAKETEXTURE_BAKE:
                    # Bake texture function
                    self.BakeTexture(node, c4d.BAKETEXTURE_BAKE)
    
                # My custom stop bake texture button 
                if id == c4d.HDRISG_BAKETEXTURE_STOP:
    
                    # Bake texture function
                    self.BakeTexture(node, c4d.BAKETEXTURE_STOP)
    
            # Check if bake texture is running
            if type == 14:  
                if data['type'] == 1001071:
                    pydata= data['data']            
                    pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
                    pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
                    PyCObject_id = pythonapi.PyCObject_AsVoidPtr(pydata)
                
                    if self.PyCObject_id != PyCObject_id:
                        self.PyCObject_id = PyCObject_id
                        checkisrunning_baking = True
                    
                    else:
                        checkisrunning_baking = False
        
        def GetVirtualObjects(self, op, hierarchyhelp):
            global checkisrunning_baking
    
            ...
    
            self.hide_baketexture_bake = c4d.HDRISG_BAKETEXTURE_BAKE if checkisrunning_baking else None
            self.hide_baketexture_stop = c4d.HDRISG_BAKETEXTURE_STOP if not checkisrunning_baking else None
    
            baking_state = "Is running baking..." if checkisrunning_baking else ""
            op[c4d.HDRISG_BAKETEXTURE_STATICTEXT] = baking_state
    
            dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA)
            if dirty is False: return op.GetCache(hierarchyhelp)
    
        def BakeTexture(self, op, event):
                obj = op.GetDown()
    
                tag = obj.GetTag(c4d.Tbaketexture)
    
                tag[c4d.BAKETEXTURE_FORMAT] = c4d.FILTER_HDR        
                tag[c4d.BAKETEXTURE_CHANNEL_REFLECTION] = True
    
                c4d.CallButton(tag, event)
    
                return tag
    
        def GetDEnabling(self, node, id, t_data, flags, itemdesc):
            
            if id[0].id == self.hide_bake_button:
                return False
    
            if id[0].id == self.hide_stop_bake_button:
                return False
    


  • Hi @mfersaoui, using the Bake Take is not the way to go to do texture baking programmatically. Since as you discovered you have no control over it. And as you execute CallButton it forces you to do the baking operation from the main thread (that's why CheckIsRunning is failing).

    So the correct way is to use c4d.utils.BakeTexture, you can find an example in the github repository: py-texture_baker_r18.
    The preview can be retrieved with the BAKE_TEX_PREVIEW flag.

    If you have any questions, let me know.
    Cheers,
    Maxime.



  • Hi @m_adam, Thank you I used py-texture_baker_r18 and I wanted to display an image preview inside the dialog, so I added a custom GUI to the dialog

    bc = c4d.BaseContainer()
    bc.SetBool(c4d.BITMAPBUTTON_BUTTON, False)
    bc.SetLong(c4d.BITMAPBUTTON_BORDER, c4d.BORDER_THIN_IN)
    self.myBitButton=self.AddCustomGui(self.BAKE_PREVIEW, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_LEFT | c4d.BFV_TOP, 512, 256, bc)
    

    And from the CoreMessage() when the baking is finished I save the baked bitmap and then I set it as Image to my custom GUI Button

    bmp.Save(self.filename, self.fileformat, c4d.BaseContainer(), c4d.SAVEBIT_ALPHA)
    self.myBitButton.SetImage(self.filename, False)
    

    The problem is that my saved image is largest then the GUI button size. And I want to resize this image to fit my custom bitmap button, and without using an external python library.

    Or is it possible to set the baked bitmap as image without saving it. and then resizing it to ensure that fit the bitmap button.

    # this doesn't work
    bmp = self.textureBakerThread.bakeBmp """ bmp = <Objet c4d.bitmaps.MultipassBitmap à 0x000000001BF75850>"""
    self.myBitButton.SetImage(bmp, False)
    

    Here is the full example:

    class TextureBakerThread(c4d.threading.C4DThread):
    
        def __init__(self, doc, textags, texuvws, destuvws):
            ...
    
        def Begin(self):
    
            # Defines baking setting
            bakeData = c4d.BaseContainer()
            bakeData[c4d.BAKE_TEX_WIDTH] = 1024
            bakeData[c4d.BAKE_TEX_HEIGHT] = 512
            ...
            self.bakeData = bakeData
    
            # Initializes bake process
            bakeInfo = c4d.utils.InitBakeTexture(self.doc, self.textags, self.texuvws, self.destuvws, self.bakeData, self.Get())
            self.bakeDoc = bakeInfo[0]
            self.bakeError = bakeInfo[1]
    
            if self.bakeError != c4d.BAKE_TEX_ERR_NONE or self.bakeDoc is None:
                return False
    
            # Starts bake thread
            self.Start(c4d.THREADMODE_ASYNC, c4d.THREADPRIORITY_BELOW)
    
            return True
    
        def BakeTextureHook(self, info):
            ...
    
        def Main(self):
            ...
    
    class TextureBakerDlg(c4d.gui.GeDialog, TextureBakerHelper):
        BUTTON_BAKE = 1000
        BUTTON_ABORT = 1001
        BAKE_PREVIEW = 1002
    
        aborted = False
        textureBakerThread = None
        infoText = None
    
        def CreateLayout(self):
    
            # Defines the title
            self.SetTitle("Texture Baker")
    
            # Creates 2 buttons for Bake / Abort button
            if self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, rows=1, title="", cols=2, groupflags=0):
                self.AddButton(id=self.BUTTON_BAKE, flags=c4d.BFH_LEFT, initw=100, inith=25, name="Bake")
                self.AddButton(id=self.BUTTON_ABORT, flags=c4d.BFH_LEFT, initw=100, inith=25, name="Abort")
            self.GroupEnd()
    
            # Creates an image preview
            bc = c4d.BaseContainer()
            bc.SetBool(c4d.BITMAPBUTTON_BUTTON, False)
            bc.SetLong(c4d.BITMAPBUTTON_BORDER, c4d.BORDER_THIN_IN)
            self.myBitButton=self.AddCustomGui(self.BAKE_PREVIEW, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_LEFT | c4d.BFV_TOP, 512, 256, bc)
    
            # Creates a statics text for the status
            if self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, rows=1, title="", cols=1, groupflags=0):
                self.infoText = self.AddStaticText(id=0, initw=0, inith=0, name="", borderstyle=0, flags=c4d.BFH_SCALEFIT)
            self.GroupEnd()
    
            # Sets Button enable states so only bake button can be pressed
            self.EnableButtons(False)
    
            return True
    
    
        def CoreMessage(self, id, msg):
            # Checks if texture baking has finished
            if id == PLUGIN_ID:
    
                # Checks if texture baking has finished
                if id == PLUGIN_ID:
                    # Sets Button enable states so only bake button can be pressed
                    self.EnableButtons(False)
    
                    # If not aborted, means the baking finished
                    if not self.aborted:
                        # Updates the information status
                        self.SetString(self.infoText, str("Baking Finished"))
    
                        # Retrieves the baked bitmap
                        bmp = self.textureBakerThread.bakeBmp
                        if bmp is None:
                            raise RuntimeError("Failed to retrieve the baked bitmap.")
    
                        # Displays the bitmap
                        c4d.bitmaps.ShowBitmap(bmp)
    
                        # Save the bitmap
                        bmp.Save(self.filename, self.fileformat, c4d.BaseContainer(), c4d.SAVEBIT_ALPHA)
                    
                        self.myBitButton.SetImage(self.filename, False)
    
                        # Removes the reference to the C4D Thread, so the memory used is free
                        self.textureBakerThread = None
                        return True
                    else:
                        # If baking is aborted, updates the information status
                        self.SetString(self.infoText, str("Baking Aborted"))
    
                    return True
            return c4d.gui.GeDialog.CoreMessage(self, id, msg)
    


  • Hi,

    BitmapButton.SetImage() works just fine. Here is an example on how to set a BitmapButton to a BaseBitmap that has been scaled to fit a target size.

    import c4d
    from c4d import bitmaps
    
    BUTTONIZE = 128
        
    class TextureBakerDlg(c4d.gui.GeDialog):
    
        def CreateLayout(self):
    
            # Creates an image preview
            bc = c4d.BaseContainer()
            bc.SetBool(c4d.BITMAPBUTTON_BUTTON, False)
            bc.SetLong(c4d.BITMAPBUTTON_BORDER, c4d.BORDER_THIN_IN)
            self.myBitButton=self.AddCustomGui(1000, c4d.CUSTOMGUI_BITMAPBUTTON, "", 
                                               c4d.BFH_LEFT | c4d.BFV_TOP,
                                               BUTTONIZE, BUTTONIZE,
                                               bc)
            
            # Set up two bitmaps, the source bitmap (it is just the maxon logo in the top 
            # left corner of the forum) and a target sized empty bitmap                  
            src_bmp = bitmaps.BaseBitmap()
            src_bmp.InitWith(
                "https://plugincafe.maxon.net/assets/uploads/system/site-logo.png")
            dst_bmp = bitmaps.BaseBitmap()
            dst_bmp.Init(BUTTONIZE, BUTTONIZE)
            
            # Scale copy our source into the target.
            src_bmp.ScaleIt(dst_bmp, 256, True, True)
            
            # Set your button.
            self.myBitButton.SetImage(dst_bmp)
    
            return True
    
    def main():
        dlg = TextureBakerDlg()
        dlg.Open(c4d.DLG_TYPE_ASYNC)
    
    # Execute main()
    if __name__=='__main__':
        main()
    

    The fact that you do not see anything / that it 'does not work' for you is probably caused by the fact that you are passing a MultipassBitmap to your button. Check MultipassBitmap.GetLayers on how to access individual layers of BaseBitmaps in your MultipassBitmap which you could then hand to your BitmapButton.

    Cheers
    zipit



  • Hi,
    Thank you very much @zipit and have a good day.


Log in to reply