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!