Smoothing vertex map



  • On 15/12/2017 at 08:00, xxxxxxxx wrote:

    In a ObjectData plugin, I create a vertex map from a polygon selection and put that in a vertex map tag.
    No problem so far, except that all the weights are set to 0% or 100%. There is no smoothing!

    So, how can I smooth the weights in the vertex map tag?

    I tried to do it using the paint tool, see below, but that gave me the warning "RuntimeError: illegal operation, invalid cross-thread call".
    Is there another way to smooth the vertex map?

    tool = c4d.plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)
    if tool is not None:
       tool[c4d.ID_BRUSH_BASE_TOOL_STRENGTH] = 1.0
       tool[c4d.ID_CA_PAINT_TOOL_MODE] = c4d.ID_CA_PAINT_TOOL_MODE_SMOOTH #smooth
       c4d.CallButton(tool, c4d.ID_CA_PAINT_TOOL_APPLY_ALL)
    


  • On 15/12/2017 at 08:22, xxxxxxxx wrote:

    I guess you already know, but for peoples who get here, I told it .But it fail cause you are using c4d.CallButton from a threaded function (probably GetVirtualObject in your case).

    A workaround could be to use Message function explain here. But I'm really not sure it will work since it's a tool and not a NodeData in your case.

    But it's maybe time for asking how SendModelingCommand work? ^^



  • On 15/12/2017 at 08:35, xxxxxxxx wrote:

    Thanks for the reply.
    I considered SendModelingCommand, but I could not see how to use the paint tool and SendModelingCommand.

    Hopefully the guys from Maxon can explain.



  • On 18/12/2017 at 12:25, xxxxxxxx wrote:

    Hi Pim,

    As gr4ph0s already told you, it's not allowed to interact with a tool from an object plugin. Also the Paint Tool can't be used with SendModelingCommand().

    The solution is to manually smooth the weights just like the Paint Tool does when clicking the Apply buttons.

    Don't be afraid, the needed code isn't complex.
    You can find a script below. It first initializes the selected points with a weight of 1.0. Then it performs the smoothing with the average of the neighboring points weight.
    Code works with a polygon object which has polygons selected.

    Note if you'll be using this code in a generator, then don't call SetBit(c4d.BIT_ACTIVE) on the vertex map tag and EventAdd().
    These functions are forbidden there and also it would not make sense to call these as the vertex map tag is used virtually.

    import c4d
      
      
    def main() :
        # Requirements
        
        if op is None:
            return
        
        if op.GetType() != c4d.Opolygon:
            return
        
        if doc.GetMode() != c4d.Mpolygons:
            return
        
        # Retrieve polygon object information
        
        allPolys = op.GetAllPolygons()
        polyCount = op.GetPolygonCount()
        if len(allPolys) != polyCount:
            return
        
        polySel = op.GetPolygonS()
        
        # Select points from polygon selection
        pointSel = c4d.BaseSelect()
        for polyIdx in xrange(polyCount) :
            if not polySel.IsSelected(polyIdx) :
                continue
            
            pointSel.Select(allPolys[polyIdx].a)
            pointSel.Select(allPolys[polyIdx].b)
            pointSel.Select(allPolys[polyIdx].c)
            pointSel.Select(allPolys[polyIdx].d)
        
        # Add vertex map tag to polygon object
        pointCount = op.GetPointCount()
        vmapTag = op.MakeVariableTag(c4d.Tvertexmap, pointCount)
        if vmapTag is None:
            return
        
        # Initialize vertex map for selected points with 1.0 weight
        vmapData = vmapTag.GetAllHighlevelData()
        for pointIdx in xrange(pointCount) :
            if not pointSel.IsSelected(pointIdx) :
                continue
            
            vmapData[pointIdx] = 1.0
        
        # Create list for the new vertex map data
        newData = [0.0] * pointCount
        
        # Strength of weight application (Opacity in Paint Tool)
        applyStrength = 1.0
        
        # Create neighbor to obtain information on neighboring polygons for each point
        nb = c4d.utils.Neighbor()
        if not nb.Init(op) :
            return
        
        # Smooth calculation loop
        for pointIdx in xrange(pointCount) :
            if not pointSel.IsSelected(pointIdx) :
                continue
            
            # Get neighboring polygons
            polys = nb.GetPointPolys(pointIdx)
            if len(polys) == 0:
                newData[pointIdx] = vmapData[pointIdx]
                continue
      
            # Select points from neighboring polygons
            sel = c4d.BaseSelect()
            for poly in polys:
                sel.Select(allPolys[poly].a)
                sel.Select(allPolys[poly].b)
                sel.Select(allPolys[poly].c)
                sel.Select(allPolys[poly].d)
            
            weight = 0.0
            num = 0
            
            # Calculate average weight from neighboring points
            segCount = sel.GetSegments()
            for segIdx in xrange(segCount) :
                first, last = sel.GetRange(segIdx, pointCount)
                for idx in range(first, last+1) :
                    weight += vmapData[idx]
                    num += 1
            
            if num > 0:
                weight /= float(num)
            
            # Blend weight
            data = vmapData[idx]
            value = data + (weight - data) * applyStrength
            
            # Set new weight
            newData[pointIdx] = c4d.utils.ClampValue(value, 0.0, 1.0)
      
        # Finally set the new vertex map data
        vmapTag.SetAllHighlevelData(newData)
        # Update polygon object
        op.Message(c4d.MSG_UPDATE)
        
        vmapTag.SetBit(c4d.BIT_ACTIVE)
        c4d.EventAdd()
      
      
    if __name__=='__main__':
        main()
    


  • On 18/12/2017 at 13:07, xxxxxxxx wrote:

    Great, thank you!


Log in to reply