Using memoryview to assign colors to vertex data

Hi, I’m trying to render an arbitrary surface mesh with an arbitrary RGBA color specification for every vertex.

So far, I was able to generate an uncolored surface with the code below:

        # vertex coordinates to memory
        mycoords = array.array("f", lrxyz.reshape(-1))
        
        # store vertex coordinates
        vertex_format = GeomVertexFormat.get_v3()
        vertex_data = GeomVertexData("vertex_data", vertex_format, Geom.UH_static)
        vertex_data.unclean_set_num_rows(lrxyz.shape[0])
        vertex_array = vertex_data.modify_array(0)
        vertex_memview = memoryview(vertex_array).cast("B").cast("f")
        vertex_memview[:] = mycoords
        
        # triangle information to memory
        faces_array = array.array("I", lrt.reshape(-1))\
        
        # store face indices
        tris_prim = GeomTriangles(GeomEnums.UH_static)
        tris_prim.set_index_type(GeomEnums.NT_uint32)
        tris_array = tris_prim.modify_vertices()
        tris_array.unclean_set_num_rows(len(faces_array))
        tris_memview = memoryview(tris_array).cast('B').cast('I')
        tris_memview[:] = faces_array
        
        # construct geometry
        surface_geometry = Geom(vertex_data)
        surface_geometry.addPrimitive(tris_prim)
        
        # construct node to hold the geometry
        surface_node = GeomNode("surface_node")
        surface_node.addGeom(surface_geometry, RenderState.makeEmpty())
        
        surface_nodePath = self.render.attachNewNode(surface_node)

And now I’m not really sure how to move forward to adding color, I was thinking of replacing the vertex format with GeomVertexFormat.get_v3c4(). However, the array format of that object is [ vertex(3f) color(4b) ] and I’m not sure how to fill that with numpy arrays.

Essentially, I have the coordinates as an nx3 numpy array of floats, and colors (rgba) in a nx4 array of either floats or ints. But am not sure how to combine these to into the memoryview insertion.

Any help would be greatly appreciated, and sorry if this seems like a simple question as I’m generally new to using memoryview in python.

Thanks

Hi, welcome to the forums!

get_v3c4() is an interleaved format containing two different data types. This is tricky to manipulate in this way.

You have several options:

  1. Create a memoryview in B format so you can store the colours as bytes instead, either doing it separately from the float data or encoding the float data as bytes before you put it into the memoryview.
  2. Create a custom GeomVertexFormat storing the colours as 4 floats instead. If your source data is in floats, this may be easiest, although it is less memory-efficient than 4 bytes, of course.
  3. Create a custom GeomVertexFormat containing two arrays, one with the float data, and one with the byte data. You can then call modify_array(1) to get a handle to this second array and create a second memoryview with the B format. You would create the vertex format like so:
vertex_format = GeomVertexFormat()
vertex_format.add_array(GeomVertexFormat.get_v3().arrays[0])
vertex_format.add_array(GeomVertexArrayFormat('color', 4, Geom.NT_uint8, Geom.C_color))
vertex_format = GeomVertexFormat.register_format(vertex_format)
2 Likes

Thank you very much for your help! The third option works really smoothly and I’m amazed by how fast the rendering works!

Just in case anyone get’s here to check what worked, here’s an example script that worked for me:

        # available information to memory
        self.mycoords = array.array("f", lrxyz.reshape(-1))
        self.mycolors = array.array("B", (255*arbitrary_colors.reshape(-1)).astype(np.uint8))

        # specify a generic vertex format
        self.vertex_format = GeomVertexFormat()
        # add 3d coordinates to the format
        self.vertex_format.add_array(GeomVertexFormat.get_v3().arrays[0])
        # add color information to the format
        self.vertex_format.add_array(GeomVertexArrayFormat('color', 4, Geom.NT_uint8, Geom.C_color))
        # register format
        self.vertex_format = GeomVertexFormat.register_format(self.vertex_format)
        
        # create data using this format
        self.vertex_data = GeomVertexData("vertex_data", self.vertex_format, Geom.UH_static)        
        # set the number of vertices according to the surface mesh
        self.vertex_data.unclean_set_num_rows(lrxyz.shape[0])
        
        # get memoryview handles to data and fill by the available information
        self.vertex_array = self.vertex_data.modify_array(0)
        self.vertex_memview = memoryview(self.vertex_array).cast("B").cast("f")
        self.vertex_memview[:] = self.mycoords
        self.color_array = self.vertex_data.modify_array(1)
        self.color_memview = memoryview(self.color_array).cast("B")
        self.color_memview[:] = self.mycolors

and with a bit of extra work, here’s how amazing the results look!

2 Likes