Bodypaint Layer Bitmap [SOLVED]



  • On 04/12/2014 at 02:16, xxxxxxxx wrote:

    Hello,

    I hope someone can help me with this!
    I can´t find an easy way to get a Bitmap out of a Bodypaint layer.
    I want to show those Layers on an Userarea, how can I do this?
    Additionally it seems to me that some bodypaint features are not available in Python?
    (at the bottom of the thread)

    What I´ve found out so far:

    if there is an active BB texture;

      
      tex = bodypaint.PaintTexture.GetSelectedTexture()  
      texturename = tex.GetFilename()  
      actlay = tex.GetActive()  
    

    if one have to load a new BB texture;

      
      #loaddialog  
      texturename =  c4d.storage.LoadDialog(type= c4d.FILESELECTTYPE_IMAGES, title="Load Hdr", flags=c4d.FILESELECT_LOAD)  
        
      #pass it to BB and make itself and the first layer active  
      settings = c4d.BaseContainer()  
      settings.SetFilename(c4d.LOADTEXTURE_FILENAME, texturename)  
      tex = bodypaint.SendPainterCommand(c4d.PAINTER_LOADTEXTURE, doc=doc, tex=None, bc=settings)  
      if not tex:return  
      lay = tex.GetFirstLayer()  
      tex.SetActiveLayer(lay, activatetexture=True, show=True)  
        
      #get the active layer  
      actlay = tex.GetActive()  
      print actlay, "layer"  
        
      #read layerdimensions  
      res =  actlay.GetBoundingBox()  
      xres = res['x2']   
      yres = res['y2']  
        
        
      #check attributes  
      print actlay[c4d.ID_BASELIST_NAME]  
      print actlay[c4d.ID_PAINTBITMAP_PREVIEW]  
      print actlay[c4d.ID_PAINTLAYER_BLEND]  
      print actlay[c4d.ID_PAINTLAYER_OPACITY]  
      print actlay[c4d.ID_PAINTLAYER_SHOW]  
        
      #add a new layer  
      print tex.AddLayerBmp(insertafter=ActLay, useundo=True, activate=True)  
        
      ######################################################################  
      #save the whole texture and pass it to the userarea is not what I want  
      ######################################################################  
      
      #orig = bitmaps.BaseBitmap()  
      #orig.InitWith(texturename)  
      #bitmaps.ShowBitmap(orig)  
      ######################################################################  
    

    -How to add a new layer on top of the last one? Why only insertafter?
    -[c4d.ID_PAINTBITMAP_PREVIEW]is always None?

    Thanks in advance
    Martin



  • On 04/12/2014 at 06:26, xxxxxxxx wrote:

    Hello,

    to get the raw content of a bitmap layer use GetPixelCnt(). Then you can copy this data into a BaseBitmap with SetPixelCnt(). You find examples how to use this functions in the painting examples in the C++ and Python SDK like SculptPaintBrush.

      
      if actlay.IsInstanceOf(c4d.OBJECT_PAINTLAYERBMP) == True:  
            
          bmp = actlay.ToPaintLayerBmp()  
                
          xres = bmp.GetBw()  
          yres = bmp.GetBh()  
          size = xres * yres  
            
          if size == 0:  
              return  
               
          colorMode = bmp.GetColorMode()     
               
          # prepare storage  
          sq = storage.ByteSeq(None, size*c4d.COLORBYTES_RGB)  
            
          inc = 3  
            
          # read data  
          for row in xrange(yres) :  
              offset = sq.GetOffset(row*(xres*inc))  
              bmp.GetPixelCnt(0, row, xres, offset, colorMode, c4d.PIXELCNT_0)   
        
          # prepare bitmap  
          baseBitmap = bitmaps.BaseBitmap()  
          baseBitmap.Init(xres,yres,)  
        
          # write data  
          for row in xrange(yres) :  
              offset = sq.GetOffset(row*(xres*inc))   
              baseBitmap.SetPixelCnt(0, row, xres, offset, inc,colorMode, c4d.PIXELCNT_0)   
            
          # save  
          filename = c4d.storage.SaveDialog(title="Save preview")  
          baseBitmap.Save(filename,c4d.FILTER_TIF)  
    

    AddLayerBmp() should accept None as an argument for "insertafter". This is a bug. ID_PAINTBITMAP_PREVIEW returns a CUSTOMDATATYPE_BITMAPBUTTON which is currently not supported in Python.

    Best wishes,
    Sebastian



  • On 04/12/2014 at 07:00, xxxxxxxx wrote:

    Hello Sebastian,

    Thank you very much! Tremendous help as always!
    Just two hours ago I started with storage and bytesequence, but could´nt get it to work.
    I tried it with the new R16 function
    PaintLayerBmp.GetPixelCnt

      
      sq = storage.ByteSeq(None, xres*yres*inc)  
      ActLay.GetPixelCnt(0, 0, cnt, sq , dstmode= c4d.COLORMODE_RGB, flags= c4d.PIXELCNT_0)  
    

    But no luck! Do you have a working example ?

    AddLayerBmp() does not accept None.
    the resulting errormessage:
    TypeError: argument 1 must be c4d.modules.bodypaint.PaintLayer, not None
    Could you please give it a try?

    Thanks again
    Martin



  • On 04/12/2014 at 07:43, xxxxxxxx wrote:

    Hi Martin,

    Here a script that read a bitmap file, converts it to spherical and writes it back.

      
    import c4d, math
    from c4d import bitmaps, gui, storage
    from c4d import plugins
      
    #
    # spherical projection
    # y=y, x=x*cos(y)
    #
      
    BUTTON_ID_SELECT = 1001 
    MY_PATH = 1003
    MY_FILEDIMENSIONS = 1007
    MY_GOBUTTON = 100014
    MY_LINKBOX = 100015
      
    #global variables
    width = 0
    height = 0
    orig = 0   
    path = None
      
    Tolerance = 0.0000001
    masterRadius = 1
    derivedRadius = math.pi/2
        
    class YourDialog(gui.GeDialog) :
      
        def CreateLayout(self) : 
           
            self.SetTitle("Spherical Projection.")
      
            self.GroupBegin(id=8000, flags=c4d.BFH_LEFT, cols=2)     #, rows=1)
            self.GroupBorderSpace(10, 10, 10, 20)
            
            self.AddButton(BUTTON_ID_SELECT, flags=c4d.BFH_LEFT, initw=145, inith=30, name="Select Input file")
            self.element = self.AddStaticText(id=MY_PATH, flags=c4d.BFH_MASK, initw=600, name="   ", borderstyle=c4d.BORDER_NONE)
            self.element = self.AddStaticText(id=11004, flags=c4d.BFH_MASK, name="File Dimensions   ", borderstyle=c4d.BORDER_NONE)
            self.element = self.AddStaticText(id=MY_FILEDIMENSIONS, flags=c4d.BFH_MASK, initw=600, name="   ", borderstyle=c4d.BORDER_NONE)
            
            self.AddSeparatorH(inith=80)
            self.AddSeparatorH(inith=80)
      
            self.AddButton(MY_GOBUTTON, c4d.BFV_MASK, initw=145, name="Go!")
      
            self.GroupEnd()    
                
      
            return True
      
        def Command(self, id, msg) :
        
            global width, height, orig, path, copy
            
            if (id == BUTTON_ID_SELECT) : 
                path = storage.LoadDialog(type=c4d.FILESELECTTYPE_IMAGES, title="Please Choose a 32-bit Image:")
                print "Path: ", path
                
                if (path != None) :
      
                    # Create and initialize selected image
                    orig = bitmaps.BaseBitmap()
                    if orig.InitWith(path)[0] != c4d.IMAGERESULT_OK:
                        gui.MessageDialog("Cannot load image \"" + path + "\".")
                        return
                    
                    # Get selected image infos
                    width, height = orig.GetSize()
                    #print "w+h: ", width, " - ", height
                    
                    bits = orig.GetBt()
                    #print "bt: ", bits
                
                    self.SetString(MY_PATH, path)
                    self.SetString(MY_FILEDIMENSIONS, str(width)+"X"+str(height)+ "  Bits: "+str(bits))
     
                # Create the copy and initialize it
                copy = bitmaps.BaseBitmap()
                copy.Init(width, height, bits)
      
            #return True
                
            if (id == MY_GOBUTTON) : 
                #c4d.EventAdd()
                #self.Close()
      
                #print "Gobutton path: ", path
                if (path == None) :
                    gui.MessageDialog ("No Input File selected!")
                    return -1   
                
                ret = SphericalProjection(self.GetString(MY_PATH))
      
                #print "Showbitmaps orig + copy"
                bitmaps.ShowBitmap(orig)
                bitmaps.ShowBitmap(copy)    
                
            return True
      
        def DestroyWindow(self) :        #Use this method to toggle the switch back to it's original off state
            #obj[c4d.ID_USERDATA, 1]=0
            c4d.EventAdd() 
      
    def SphericalProjection(path) :
      
        global width,height,orig
        
        c4d.gui.SetMousePointer(c4d.MOUSE_BUSY)
      
        if not path: return
         
        height2 = height / 2
        width2 = width / 2
      
        
        for ix in range(width) :  
            for iy in range(height) :
                ycos = math.sin(math.radians(90.0/height2)*iy)
                x = width2 + (width2-ix)*ycos
                r,g,b = orig.GetPixel(int(x), iy)
                copy.SetPixel(width-ix, iy, r, g, b) 
      
        return True
     
    if __name__=='__main__':
        
        dlg = YourDialog()
        dlg.Open(dlgtype= c4d.DLG_TYPE_ASYNC, defaultw=400)       #, xpos=600, ypos=500, defaultw=200, defaulth=200)
      
    
    


  • On 04/12/2014 at 08:53, xxxxxxxx wrote:

    Hello Martin,

    do you have any questions about the above example or the linked examples?

    As stated above, it is a bug that AddLayerBmp() does not accept None.

    Best wishes,
    Sebastian



  • On 04/12/2014 at 09:27, xxxxxxxx wrote:

    @Sebastian
    ahh,
    I see, I assigned the bug statement to the bitmapbutton for whatever reason.Thanks.

    The example is pretty clear.
    You just used the new GetPixelCnt method?
    Thanks!

    For the Limit list on support site, should I write an email that it´s not possible in Python, or will this restrictions registered automatically?

    @Pim
    Thanks!

    Best wishes
    Martin



  • On 05/12/2014 at 01:50, xxxxxxxx wrote:

    Hello,

    just in case someone needs to know.
    To avoid the layer insertion point bug, one can use GeListNode class commands like:

      
      #add a new layer  
      newlayer = tex.AddLayerBmp(insertafter=actlay, useundo=True, activate=True)  
      # as (insertafter= None) does not work a workaround to paste the new layer on top of the list  
      newlayer.InsertBefore(tex.GetFirstLayer())  
      #to avoid problems with previous loaded textures, set the first layer activ  
      first = tex.GetFirstLayer()  
      tex.SetActiveLayer(first, activatetexture=True, show=True)  
    

    @Sebastian
    could you please explain what´s the difference between GetPixelCnt with a Bitmap and a BodyPaint layer?
    I didn´t get it.

    Thanks in advance
    Martin



  • On 05/12/2014 at 05:42, xxxxxxxx wrote:

    Hello,

    PaintLayerBmp.GetPixelCnt() and BaseBitmap.GetPixelCnt() are two different methods of two different classes. They are used pretty much the same way; the only difference is that BaseBitmap.GetPixelCnt() allows you to define a byte increment per pixel for the output buffer.

    best wishes,
    Sebastian


Log in to reply