Solved Trying to programmatically create vertex color tag

Hi,
I am making a python script to import a mesh in OBJ format, and want to programmatically set the colors on a per-vertex basis.

I am currently playing around, have imported the mesh using File->Merge Project, set a single material for all objects (there is only one object), and got a reference to the polygons by dragging into the python command line (op = Default below).

I followed examples I found (code below) to make a vertex color tag, and just to experiment set the vertices to red. The issues :

  1. I have to select the vertex color tag in the Objects panel for the coloring to show up, otherwise the color is just default white

  2. When the tag is selected there is no shading, color is 'flat'

  3. Rendering just shows the white underlying material coloring

Code is below, what am I missing?

Thanks,

Randy

op = Default 
red = c4d.Vector4d(1.0, 0.0, 0.0, 1.0)

pointCount = op.GetPointCount()
tag = c4d.VertexColorTag(pointCount)

tag.SetPerPointMode(True)

dataW = tag.GetDataAddressW()

for idx in range(pointCount) :
	c4d.VertexColorTag.SetPoint(dataW, None, None, idx, red)

op.InsertTag(tag)
tag.SetBit(c4d.BIT_ACTIVE)

c4d.EventAdd()

Hello @zauhar,

thank you for reaching out to us. Your code does look mostly fine and is working for me. The line op = Default makes little sense and you are of course missing things like imports, a context guard and a main function. Not all of them are absolutely necessary, but it is strongly advised to use them all, especially for novice users. Find a code example below.

Vertex colors are also not automatically rendered as the diffuse color in Cinema 4D, one must use a VertexMap shader for that. Please consult the user manual or end-user support for end-user questions, we cannot deliver such support here.

Cheers,
Ferdinand

The result for two polygon objects, the color of a vertex depends on its position in global space.
0e9b3117-e70a-4495-8001-cbe0a6b0a2e4-image.png

Code

"""Creates vertex-color tag on the currently selected polygon object and makes it the
active tag.

Must be run as a Script Manager scrip with a polygon object selected. The color of each vertex will
depend on its position in global space.
"""

import c4d
import typing

GRADIENT_MIN: float = -200.0 # The lower boundary of the gradient.
GRADIENT_MAX: float =  200.0 # The upper boundary of the gradient.

op: typing.Optional[c4d.BaseObject] # The active object, can be `None`.

def main() -> None:
    """Runs the example.
    """
    # Check that there is indeed a selected object and that it is a polygon object.
    if not isinstance(op, c4d.PolygonObject):
        raise TypeError("Please select a polygon object.")

    # Get its point count and allocate a vertex color tag with that count.
    count: int = op.GetPointCount()
    tag: c4d.VertexColorTag = c4d.VertexColorTag(count)
    if not isinstance(tag, c4d.VertexColorTag):
        raise MemoryError("Could not allocate tag.")

    # We are going to make it a little bit more exciting and write a gradient based on the global
    # coordinate y-component of a vertex.

    # Set the tag to defining colors only once per vertex instead of having N colors per vertex,
    # where N is the number of polygons attached to it.
    tag.SetPerPointMode(True)

    # Get the global matrix of the polygon object and all its points in global space.
    mg: c4d.Matrix = op.GetMg()
    globalPoints: list[c4d.Vector] = [mg * p for p in op.GetAllPoints()]

    # Define a callable with which we can get the gradient color of a point over its index.
    GetGradientColor = lambda i : c4d.Vector4d(
        c4d.utils.RangeMap(globalPoints[i].y, GRADIENT_MIN, GRADIENT_MAX, 0, 1, True), 0, 0, 1)

    # Get the data pointer of the tag and start writing data.
    dataW: object = tag.GetDataAddressW()
    for i in range(count):
        c4d.VertexColorTag.SetPoint(dataW, None, None, i, GetGradientColor(i))

    # Insert the tag into the selected object, make it the active tag, and push an update event.
    op.InsertTag(tag)
    tag.SetBit(c4d.BIT_ACTIVE)
    c4d.EventAdd()

if __name__ == "__main__":
    main()

MAXON SDK Specialist
developers.maxon.net

Hello @zauhar,

thank you for reaching out to us. Your code does look mostly fine and is working for me. The line op = Default makes little sense and you are of course missing things like imports, a context guard and a main function. Not all of them are absolutely necessary, but it is strongly advised to use them all, especially for novice users. Find a code example below.

Vertex colors are also not automatically rendered as the diffuse color in Cinema 4D, one must use a VertexMap shader for that. Please consult the user manual or end-user support for end-user questions, we cannot deliver such support here.

Cheers,
Ferdinand

The result for two polygon objects, the color of a vertex depends on its position in global space.
0e9b3117-e70a-4495-8001-cbe0a6b0a2e4-image.png

Code

"""Creates vertex-color tag on the currently selected polygon object and makes it the
active tag.

Must be run as a Script Manager scrip with a polygon object selected. The color of each vertex will
depend on its position in global space.
"""

import c4d
import typing

GRADIENT_MIN: float = -200.0 # The lower boundary of the gradient.
GRADIENT_MAX: float =  200.0 # The upper boundary of the gradient.

op: typing.Optional[c4d.BaseObject] # The active object, can be `None`.

def main() -> None:
    """Runs the example.
    """
    # Check that there is indeed a selected object and that it is a polygon object.
    if not isinstance(op, c4d.PolygonObject):
        raise TypeError("Please select a polygon object.")

    # Get its point count and allocate a vertex color tag with that count.
    count: int = op.GetPointCount()
    tag: c4d.VertexColorTag = c4d.VertexColorTag(count)
    if not isinstance(tag, c4d.VertexColorTag):
        raise MemoryError("Could not allocate tag.")

    # We are going to make it a little bit more exciting and write a gradient based on the global
    # coordinate y-component of a vertex.

    # Set the tag to defining colors only once per vertex instead of having N colors per vertex,
    # where N is the number of polygons attached to it.
    tag.SetPerPointMode(True)

    # Get the global matrix of the polygon object and all its points in global space.
    mg: c4d.Matrix = op.GetMg()
    globalPoints: list[c4d.Vector] = [mg * p for p in op.GetAllPoints()]

    # Define a callable with which we can get the gradient color of a point over its index.
    GetGradientColor = lambda i : c4d.Vector4d(
        c4d.utils.RangeMap(globalPoints[i].y, GRADIENT_MIN, GRADIENT_MAX, 0, 1, True), 0, 0, 1)

    # Get the data pointer of the tag and start writing data.
    dataW: object = tag.GetDataAddressW()
    for i in range(count):
        c4d.VertexColorTag.SetPoint(dataW, None, None, i, GetGradientColor(i))

    # Insert the tag into the selected object, make it the active tag, and push an update event.
    op.InsertTag(tag)
    tag.SetBit(c4d.BIT_ACTIVE)
    c4d.EventAdd()

if __name__ == "__main__":
    main()

MAXON SDK Specialist
developers.maxon.net