pythonblender

Populate a blender PointCloud via python API with numpy data


I can create a PointCloud object in the blender python API via

pc_data = bpy.data.pointclouds.new(name)
pc_obj = bpy.data.objects.new(name, pc_data)
bpy.context.collection.objects.link(pc_obj)

Problem is, that does not give me any points to work with. There is stuff like pc_data.points.foreach_set("vector", my_np_array.ravel()) and pc_data.attributes.new(name='position', type='FLOAT_VECTOR', domain='POINT').foreach_set("vector", my_np_array.ravel()), which all seems like a working API but fails because the PointCloud is empty and I somehow need to resize it to the size that I need it. But how?

I have also looked into hacks, for example using the operators/OPS/UI API. The irony is that I haven't even found a way to spawn points in the UI. The only thing I can do is to select some points and then duplicate or delete them.


Solution

  • Problem: A deep dive into the python bindings in C/C++ revealed that there is currently (as of blender 4.5.1 LTS) simply no straight forward option. The C/C++-function to allocate points in the pointcloud is not used/exposed in the blender python binding C-files.

    Solution: Use a hack to allocate points via a geometry nodes modifier which is added, loaded with a previously/manually created node group (here called "[spawn]" in the picture), adjusted and applied automatically, all in blender python API.

    enter image description here

    import bpy
    
    def add_pointcloud_points_to_obj(obj, count):
        obj.modifiers.new("spawn_dummy_geonodes", "NODES")
        obj.modifiers['spawn_dummy_geonodes'].node_group = bpy.data.node_groups['[spawn]']
    
        # This is the "howmany" socket, but the name is not the key.
        # I don't know how to change the socket key
        obj.modifiers["spawn_dummy_geonodes"]["Socket_2"] = count
    
        # this applies the modifier (without bpy.ops)
        depsgraph = bpy.context.evaluated_depsgraph_get()
        obj_evaluated = obj.evaluated_get(depsgraph)
        obj.data = obj_evaluated.data.copy()
    
        obj.modifiers.remove(obj.modifiers['spawn_dummy_geonodes'])