Setting Vertex Data with memory pointer

I am assigning a list of positions and colors to geom and make a point cloud.
The following part is my code

vertex_rgbas = np.array([[0, 0, 0, 1], ]*len(vertices))
vertformat = GeomVertexFormat.getV3c4()
vertexdata = GeomVertexData(name, vertformat, Geom.UHStatic)
vertexdata.modifyArrayHandle(0).setData(np.hstack((vertices, vertex_rgbas)).astype(np.float32).tostring())

It seems the c4 is not float32 and the memory is misaligned.
Which format should I use?

It should be Vec4(), where is the float component.

It’s GeomEnums.NT_uint8, as mentioned on this manual page.

You could instead use a custom vertex format with a 4-component float column for vertex colors, but this will take up more memory.

On a side note, I strongly recommend the use of memoryviews instead of the interface you’re using right now.

EDIT
If you want to use floats, you can convert the vertex format after filling the vertex data to GeomVertexFormat.get_v3c4:

vertex_rgbas = np.array([[0, 0, 0, 1], ]*len(vertices))
vertformat = GeomVertexFormat()
arrayformat = GeomVertexArrayFormat()
arrayformat.add_column(InternalName.get_vertex(), 3, GeomEnums.NT_float32, GeomEnums.C_point)
arrayformat.add_column(InternalName.get_color(), 4, GeomEnums.NT_float32, GeomEnums.C_color)
vertformat.add_array(arrayformat)
vertformat = GeomVertexFormat.register_format(vertformat)
vertexdata = GeomVertexData(name, vertformat, Geom.UHStatic)
vertexdata.unclean_set_num_rows(len(vertices))
view = memoryview(vertexdata.modify_array(0)).cast("B").cast("f")
view[:] = np.hstack((vertices, vertex_rgbas)).astype(np.float32)
vertexdata.format = GeomVertexFormat.get_v3c4()

Alternatively, if you prefer to use 8-bit integers to fill the data directly and you don’t mind splitting the format into 2 columns, the following should be fast too (presuming the color is the same for all vertices):

vertformat = GeomVertexFormat()
arrayformat = GeomVertexArrayFormat()
arrayformat.add_column(InternalName.get_vertex(), 3, GeomEnums.NT_float32, GeomEnums.C_point)
vertformat.add_array(arrayformat)
arrayformat = GeomVertexArrayFormat()
arrayformat.add_column(InternalName.get_color(), 4, GeomEnums.NT_uint8, GeomEnums.C_color)
vertformat.add_array(arrayformat)
vertformat = GeomVertexFormat.register_format(vertformat)
vertexdata = GeomVertexData(name, vertformat, Geom.UHStatic)
vertexdata.unclean_set_num_rows(len(vertices))
pos_view = memoryview(vertexdata.modify_array(0)).cast("B").cast("f")
pos_view[:] = vertices
vertexdata = vertexdata.set_color((0, 0, 0, 1))

Your final choice is to interleave the position data with the color data (in the [0, 255] integer range) somehow (in pure Python struct can be used for this).

1 Like

Example of the blue polygon.

from panda3d.core import GeomVertexData, GeomVertexFormat, Geom, GeomTriangles, GeomVertexWriter, GeomNode
from direct.showbase.ShowBase import ShowBase

class MyApp(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        
        vdata = GeomVertexData('name', GeomVertexFormat.getV3c4(), Geom.UHStatic)
        vdata.setNumRows(2)

        vertex = GeomVertexWriter(vdata, 'vertex')
        color = GeomVertexWriter(vdata, 'color')

        vertex.addData3(1, 0, 0)
        color.addData4(0, 0, 1, 1)

        vertex.addData3(1, 1, 0)
        color.addData4(0, 0, 1, 1)

        vertex.addData3(0, 1, 0)
        color.addData4(0, 0, 1, 1)

        vertex.addData3(0, 0, 0)
        color.addData4(0, 0, 1, 1)

        prim = GeomTriangles(Geom.UHStatic)
        prim.addVertices(0, 1, 2)
        prim.addVertices(0, 2, 3)
        prim.closePrimitive()

        geom = Geom(vdata)
        geom.addPrimitive(prim)

        node = GeomNode('gnode')
        node.addGeom(geom)

        render.attachNewNode(node)

app = MyApp()
app.run()
1 Like

You could modify your GeomVertexFormat to instead use a floating-point column, if that’s easier for you.

By the way, this is inefficient:

You can instead use copyDataFrom, which accepts a buffer object:
vertexdata.modifyArrayHandle(0).copyDataFrom(np.hstack((vertices, vertex_rgbas)), size_in_bytes)

Or, you can use memoryview(vertexdata.modifyArray(0)), which will give you a memoryview object you can use for copying.

1 Like

Thanks all! Here is the final code for reference.

vertformat = GeomVertexFormat()
arrayformat = GeomVertexArrayFormat()
arrayformat.addColumn(InternalName.getVertex(), 3, GeomEnums.NTFloat32, GeomEnums.CPoint)
vertformat.addArray(arrayformat)
arrayformat = GeomVertexArrayFormat()
arrayformat.addColumn(InternalName.getColor(), 4, GeomEnums.NTUint8, GeomEnums.CColor)
vertformat.addArray(arrayformat)
vertformat = GeomVertexFormat.registerFormat(vertformat)
vertexdata = GeomVertexData(name, vertformat, Geom.UHStatic)
vertexdata.modifyArrayHandle(0).copyDataFrom(vertices.astype(np.float32))
vertexdata.modifyArrayHandle(1).copyDataFrom(vertex_rgbas)
primitive = GeomPoints(Geom.UHStatic)
primitive.setIndexType(GeomEnums.NTUint32)
primitive.modifyVertices(-1).modifyHandle().copyDataFrom(np.arange(len(vertices), dtype=np.uint32))
geom = Geom(vertexdata)
geom.addPrimitive(primitive)