I wanted to know whether it is possible to change the data count of a vertex map dynamically without always killing the tag and recreating it? I have a mesh that gets subdivided dynamically and I need the vertex map to always fit the mesh vertex count but I can't have the user to always re-link the vertex map on every change.
zipit last edited by zipit
this should not be necessary, since vertex maps are being automatically resized to match the point count of their hosting object. Could you provide some code or an example on where you did encounter a different behavior? Below you will find a script, that shows you how to access the high level data of a vertex map.
import c4d def main(): """ """ # Ensure that the current selection is a polygon object. if not isinstance(op, c4d.PolygonObject): return # Get the first vertex map of the object. vertextmaps = [tag for tag in op.GetTags() if tag.CheckType(c4d.Tvertexmap)] if not vertextmaps: return vmap = vertextmaps # Vertex maps are instances of c4d.VariableTag. VariableTag.GetDataCount() # returns the number of data elements of the instance, e.g. the number of # weights for a vertex map. print "vertex map data count before subdivision:", vmap.GetDataCount() # Subdivide the object once bc = c4d.BaseContainer() bc[c4d.MDATA_SUBDIVIDE_SUB] = 1 res = c4d.utils.SendModelingCommand( command=c4d.MCOMMAND_SUBDIVIDE, list=[op], mode=c4d.MODELINGCOMMANDMODE_POLYGONSELECTION, bc=bc, doc=doc) # The vertex map has been automatically resized. print "vertex map data count after subdivision:", vmap.GetDataCount() # A list of weights with the length of the size of the vertex map, where # even IDs (i.e. the point IDs of the polygon object) are a weight of 1 # and odd elements are a weight of 0. weights = [float(n % 2 == 0) for n in range(vmap.GetDataCount())] # Write the new weight data into our vertex map. vmap.SetAllHighlevelData(weights) # Update Cinema c4d.EventAdd() # Execute main() if __name__ == '__main__': main()
m_adam last edited by m_adam
Regarding your question I would ask you for more information regarding your context, are you in a CommandData, ObjectData?
Normally as @zipit said the vertex map is automatically resized when obj.Message(c4d.MESSAGE_UPDATE) is called (which is normally done for each modeling operation).
So maybe you simply need this as "subdivided dynamically" is very obscure and could mean a lot of things.
thanks m_adam and zipit for your replies.
I am working in an ObjectData Plugin.
I am loading a mesh into my ObjectData (in Init) and need to add modifiers/subdivision in GetVirtualObjects.
After Zipits answer I noticed why it didn't work properly and that is that I added the tag to my ObjectData instead of the loaded mesh, the reason for that is that I would need the user to see the vertexmap but I can't expose the mesh.
I have now changed it so the vertexmap is created on my internal mesh, and that scales just fine when subdividing, so in that regard my problem is solved.
But I don't know if I am now able to expose the tag, and only the tag, somehow to the user.
My Plugin uses the vertexmap internally and I want the user to be able to use it for materials etc.
Is there any way?
Thanks for your help so far!
m_adam last edited by
Hi @neon sorry for the delay, unfortunately, Cinema 4D is not designed this way.
If you think about it, there is no tag that works by hierarchy inheritance except the selection tag. Which work by naming a selection. So it's a very special case.
The main issue is Cinema 4D will, in any case, reset the data you define in the vertex map to the number of polygons. (and in this case, 0 since it's a generator).
So here a workaround, but it's very experimental and can be buggy. So please don't use it in production as he does some really cross-thread things that are not allowed and that could crash Cinema 4D. But at least it can give you some ideas.
And the python code of the python generator.
import c4d def Bl2DIterator(obj): """ Iterates over any BaseList2D list """ while obj: yield obj for opChild in Bl2DIterator(obj.GetDown()): yield opChild obj = obj.GetNext() def FindAllVetexShaderFromObj(obj, targetVertexMap): """ Retrieve from each material assigned to `obj` all vertexMap shader where `targetVertexMap` is used. """ allVertexShader =  for tag in obj.GetTags(): if not tag.IsInstanceOf(c4d.Ttexture): continue mat = tag[c4d.TEXTURETAG_MATERIAL] if mat is None: continue for shader in Bl2DIterator(mat.GetFirstShader()): if shader.IsInstanceOf(c4d.Xvertexmap): vMap = shader[c4d.SLA_DIRTY_VMAP_OBJECT] if vMap is not None and vMap == targetVertexMap: allVertexShader.append(shader) return allVertexShader def main(): # Retrieve a copy of linked obj polygonObj = op[c4d.ID_USERDATA,1] if not isinstance(polygonObj, c4d.PolygonObject): return polygonObj = polygonObj.GetClone() polygonObj.SetMl(op.GetMg()) # Retrieve vertex map from the copied object vMap = polygonObj.GetTag(c4d.Tvertexmap) if vMap is None: return # Subdivide the opbject bc = c4d.BaseContainer() bc[c4d.MDATA_SUBDIVIDE_SUB] = 1 res = c4d.utils.SendModelingCommand( command=c4d.MCOMMAND_SUBDIVIDE, list=[polygonObj], mode=c4d.MODELINGCOMMANDMODE_POLYGONSELECTION, bc=bc, doc=polygonObj.GetDocument()) # Assign random values weights = [float(n % 4 == 0) for n in range(vMap.GetDataCount())] vMap.SetAllHighlevelData(weights) # Retrieve existing vertexMap and shader from the applied material that use this vertex map shaderUsingOldVertexMap =  tMapOld = op.GetTag(c4d.Tvertexmap) if tMapOld is not None: shaderUsingOldVertexMap += FindAllVetexShaderFromObj(op, tMapOld) tMapOld.Remove() # Creates a new tag with the correct number of data and disable it so C4D don't overwrite it # Cross-thread Operation, that could crash Cinema 4D vMapGenerator = op.MakeVariableTag(c4d.Tvertexmap, polygonObj.GetPointCount()) vMapGenerator[c4d.EXPRESSION_ENABLE] = False # Copy the data from the vertex map within the generator to the one exposed to the user vMapGenerator.SetAllHighlevelData(vMap.GetAllHighlevelData()) # Remap all the vertex shader to use the new vertex map. for shader in shaderUsingOldVertexMap: shader[c4d.SLA_DIRTY_VMAP_OBJECT] = vMapGenerator return polygonObj
thanks for your answer!
Its too bad that I can't do it without some hacky way, but thanks for your code snipped and example.
I'll mark this thread as solved, so thanks!