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 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.
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$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$