8
$\begingroup$

How can one go about generating a point cloud from the surfaces/faces of a model?

Initially the models are going to be fairly simple (just cubes, cylinders, and spheres), but eventually the models will be progressing on to more complicated shapes.

The type of point cloud that I need to generate needs to be similar to the point cloud that would result from a laser scanner. So I do not need any edge or vertex information, just a point for every face of the mesh.

$\endgroup$
3
  • $\begingroup$ related: blender.stackexchange.com/questions/15990/… $\endgroup$
    – user1853
    Dec 7 '14 at 2:33
  • 2
    $\begingroup$ I'm not quite sure what you mean by "no edge or vertex information". Should it look like the model was 3D printed and then scanned? I.e. an arbitrary scattering of points on the surface, regardless of topology? If so, I'd recommend using a particle system. $\endgroup$
    – gandalf3
    Dec 7 '14 at 8:59
  • 3
    $\begingroup$ This sound somewhat contradictory, point cloud but no edge or vertex info, just faces. Could you add an image to show what you mean. Vertices are points in space. If you simply want only vertices press 'X' to bring up the delete popup in edit mode and select 'Only Edges & Faces' $\endgroup$
    – stacker
    Dec 7 '14 at 10:46
13
$\begingroup$

Following script creates a new point cloud from the selected object based the center of each face:

enter image description here

import bpy

def point_cloud(ob_name, coords):
    """Create point cloud object based on given coordinates and name."""
    me = bpy.data.meshes.new(ob_name + "Mesh")
    ob = bpy.data.objects.new(ob_name, me)
    me.from_pydata(coords, [], [])
    ob.show_name = True
    me.update()
    return ob

def face_centers(obj):
    """Returns median center coordinates for each face of given mesh object."""
    if obj.type == 'MESH':
        import bmesh
        bm = bmesh.new()
        bm.from_mesh(obj.data)
        return [obj.matrix_world @ f.calc_center_median() for f in bm.faces]
    else:
        return [(0.0, 0.0, 0.0)]


ob = bpy.context.active_object
pc = point_cloud(ob.name + "-pointcloud", face_centers(ob))

# Link object to the active collection
bpy.context.collection.objects.link(pc)

Related: Add a simple vertex via python?

$\endgroup$
3
  • $\begingroup$ For active collection have been using context.collection or context.scene.collection any particular reason to use context.view_layer.active_layer_collection.collection instead? $\endgroup$
    – batFINGER
    Dec 18 '19 at 13:07
  • $\begingroup$ As always, good catch @batFINGER. New to collections, I personally would expect that the new object is part of whatever collection I'm currently selected in the outliner. Your are right and C.collection reference seems to be the perfect, I didn't noticed the property in the console... Many thanks for your comment. Cheers $\endgroup$
    – p2or
    Dec 18 '19 at 14:41
  • $\begingroup$ Cheers for your prodding comments of late. Have found that C.collection can be None hence use C.collection or C.scene.collection since the latter always exists. $\endgroup$
    – batFINGER
    Dec 18 '19 at 14:45
3
$\begingroup$

Just playing around, this seems like the simplest solution, although it may have trouble on more complex models.

  1. Add a remesh modifier
  2. Change the remesh type to smooth (starts at sharp by default)
  3. Increase the octree depth to the resolution of the pointcloud you want
  4. Adjust the scale for finer changes to the resolution

To get the points, do as stacker suggested:

  1. Apply the remesh modifier
  2. Go into edit mode
  3. Select all (a)
  4. Delete edges and faces (x, select "only edges and faces")
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.