3
$\begingroup$

I have lidar data in numpy structured array with x,y,z and other lidar specifics that I can use to colourize and display.

I have got pretty far on the numpy side for filtering the points down (near, far, density) and multiplying the points to create cubes at each coordinate, that even get larger the further away they are.

I have used this as a baseline for my code, and can't really find any other examples. And it's quite fast, I can bring in large point-clouds at thousands of frames in under a few seconds.

My only issue now is that when I create the mesh, it looks fine at first! But when I enter and leave edit mode, there are all these connecting edges added.. I'm sure this due to how I'm creating the loops.

Before and after edit mode (Before and after entering edit modes)

Here's my code:

# parse and fitler the point_cloud here, such as min, max range, density, and quantize
point_cloud = self.filter_cloud(np.load(self.url + file + self.ext))
   
mesh = bpy.data.meshes.new(name=file)
linear_scale = self.get_linear_scale(point_cloud)

# Create a 8 cube vertices at each point cloud x,y,z coordinate
vertices =  np.concatenate((
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
), axis=1).ravel()

num_vertices = (vertices.shape[0] // 3)

# Create a list of polygons from those vertices using their indexes
vertex_index = np.array([
    np.arange(start=num_vertices * 3, step=8) + 0,
    np.arange(start=num_vertices * 3, step=8) + 1,
    np.arange(start=num_vertices * 3, step=8) + 3,
    np.arange(start=num_vertices * 3, step=8) + 2,

    np.arange(start=num_vertices * 3, step=8) + 1,
    np.arange(start=num_vertices * 3, step=8) + 5,
    np.arange(start=num_vertices * 3, step=8) + 4,
    np.arange(start=num_vertices * 3, step=8) + 0,

    np.arange(start=num_vertices * 3, step=8) + 2,
    np.arange(start=num_vertices * 3, step=8) + 3,
    np.arange(start=num_vertices * 3, step=8) + 7,
    np.arange(start=num_vertices * 3, step=8) + 6,

    np.arange(start=num_vertices * 3, step=8) + 3,
    np.arange(start=num_vertices * 3, step=8) + 7,
    np.arange(start=num_vertices * 3, step=8) + 5,
    np.arange(start=num_vertices * 3, step=8) + 1,

    np.arange(start=num_vertices * 3, step=8) + 4,
    np.arange(start=num_vertices * 3, step=8) + 5,
    np.arange(start=num_vertices * 3, step=8) + 7,
    np.arange(start=num_vertices * 3, step=8) + 6,

    np.arange(start=num_vertices * 3, step=8) + 6,
    np.arange(start=num_vertices * 3, step=8) + 4,
    np.arange(start=num_vertices * 3, step=8) + 0,
    np.arange(start=num_vertices * 3, step=8) + 2,
], dtype=np.int32).flatten('F')

# notate where each polygon starts
loop_start = np.array([
    np.arange(start=num_vertices * 3, step=8) + 0,
    np.arange(start=num_vertices * 3, step=8) + 1,
    np.arange(start=num_vertices * 3, step=8) + 2,
    np.arange(start=num_vertices * 3, step=8) + 3,
    np.arange(start=num_vertices * 3, step=8) + 4,
    np.arange(start=num_vertices * 3, step=8) + 6,
], dtype=np.int32).flatten('F')

# How many vertices does each polygon have?
loop_total = np.full(loop_start.shape[0], 4)

num_vertex_indices = vertex_index.shape[0]
num_loops = loop_start.shape[0]

mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", vertices)

mesh.loops.add(num_vertex_indices)
mesh.loops.foreach_set("vertex_index", vertex_index)

mesh.polygons.add(num_loops)
mesh.polygons.foreach_set("loop_start", loop_start)
mesh.polygons.foreach_set("loop_total", loop_total)


mesh.update()
mesh.validate()

obj = bpy.data.objects.new(file, mesh)
self.add_to_scene(obj, frame)

I have also tried the much slower, but more straight forwards from_pydata, but it just hangs for a while, then crashes.. (the verts and faces part works fine on it's own, it hangs and crashes at the from_pydata part)

verts = list(map(tuple, vertices.reshape((vertices.shape[0] // 3), 3)))
faces = list(map(tuple, vertex_index.reshape((vertex_index.shape[0] // 4), 4)))
mesh.from_pydata(verts, [], faces)

Any advice or documentation would be kindly appreciated! Maybe I can delete the edges programmatically? Maybe there's a way to break up the loops so they don't connect? I tried printing loops and vertex data for normal cubes and cubes with connecting non-polygon edges but they seem to be identical.

$\endgroup$
5
  • 1
    $\begingroup$ From my experience from_pydata is pretty fast, but it does bug out when the entry data is not properly formatted. Have you tested it out with only a few points to see how the different data lists look like ? $\endgroup$
    – Gorgious
    May 26, 2021 at 6:31
  • 2
    $\begingroup$ Can you provide a scan to test? $\endgroup$
    – brockmann
    May 26, 2021 at 6:56
  • 1
    $\begingroup$ If Mesh.validate() is true, there is something wrong with your data. (would perhaps call it before update) Could you provide a link to full source to test? Ok see you've answered while commenting... looks good. $\endgroup$
    – batFINGER
    May 26, 2021 at 8:45
  • 1
    $\begingroup$ Wonder if making a direct point cloud from single verts then using a particle system or duplivert object will be quicker than as above. $\endgroup$
    – batFINGER
    May 26, 2021 at 8:56
  • $\begingroup$ I was going to try the particle system as an alternate, since loading the pointcloud as verts only is way faster. I think I'll need to do some more research on using particles for this, but if I can map the other attributes to colourize each particle, then that would be perfect and more desirable than creating real mesh. The main goal is to colorize this and render various concepts to then decide what would make sense in a real time system. $\endgroup$ May 26, 2021 at 9:05

1 Answer 1

1
$\begingroup$

enter image description here

Thank you @Gorgious! On further investigation, I defs messed up something. The worst part was that it was working somewhat using the lower level mesh creation.

I gave from_pydata a longer look, it seems a bit slow, but all that matters is that it works, and I can't complain about that! Now Just need to assign the colours.

point_cloud = self.filter_cloud(np.load(self.url + file + self.ext))

linear_scale = self.get_linear_scale(point_cloud)

vertices =  np.concatenate((
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] - linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] - linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] - linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
    np.array([point_cloud['x'] + linear_scale, point_cloud['y'] + linear_scale, point_cloud['z'] + linear_scale], order="F").ravel(order="K").reshape(len(point_cloud['x']), 3),
), axis=1).ravel().reshape(-1, 3)
num_vertices = vertices.shape[0]

vertex_index = np.array([
    np.arange(start=num_vertices, step=8) + 0,
    np.arange(start=num_vertices, step=8) + 1,
    np.arange(start=num_vertices, step=8) + 3,
    np.arange(start=num_vertices, step=8) + 2,

    np.arange(start=num_vertices, step=8) + 1,
    np.arange(start=num_vertices, step=8) + 5,
    np.arange(start=num_vertices, step=8) + 4,
    np.arange(start=num_vertices, step=8) + 0,

    np.arange(start=num_vertices, step=8) + 2,
    np.arange(start=num_vertices, step=8) + 3,
    np.arange(start=num_vertices, step=8) + 7,
    np.arange(start=num_vertices, step=8) + 6,

    np.arange(start=num_vertices, step=8) + 3,
    np.arange(start=num_vertices, step=8) + 7,
    np.arange(start=num_vertices, step=8) + 5,
    np.arange(start=num_vertices, step=8) + 1,

    np.arange(start=num_vertices, step=8) + 4,
    np.arange(start=num_vertices, step=8) + 5,
    np.arange(start=num_vertices, step=8) + 7,
    np.arange(start=num_vertices, step=8) + 6,

    np.arange(start=num_vertices, step=8) + 6,
    np.arange(start=num_vertices, step=8) + 4,
    np.arange(start=num_vertices, step=8) + 0,
    np.arange(start=num_vertices, step=8) + 2,
], dtype=np.int32).flatten('F').reshape(-1, 4)

mesh = bpy.data.meshes.new(name=file)

verts = list(map(tuple, vertices))
faces = list(map(tuple, vertex_index))
mesh.from_pydata(verts, [], faces)

mesh.update()
mesh.validate()

obj = bpy.data.objects.new(file, mesh)
self.add_to_scene(obj, frame)
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .