LineSystem and Performant Pickable Meshes

I am using LineSystem to quickly render large amounts of lines. This can be 100K lines ranging from 2 points (straight line) to up to 32 points (curves). LineSystem is amazingly performant compared to CreateLines, however it does not allow me to define an ID to select individual meshes (e.g. via click events). LineSystem does create a unique faceId property that is returned in the pickInfo event object for each line/line segment, but I cannot reliably correlate that to individual lines in my rendering (at least based on how I have defined them).

I have experimented with invisible tubes/cubes over my lines, but this is still drastically affecting performance. I do like that it gives a larger ‘bounding box’ over my lines on which to register click events, but some of my larger data sets will not render at all with this approach.
(Pick individual Lines from LineSystem?)

Does anyone have any tips on retaining the performance of LineSystem, while still offering a means to click and identify individual line segments?

Thanks!

.

I am not sure it would be possible to pick in 100k “random” lines in a reasonable time to be at 60 fps. You would need some kind of octree like structure and so on…

This is what I am afraid of, I just thought I may be overlooking a simple way to accomplish this. The engine is already creating unique ‘faceId’ values for each line/line segment, however since we pass in all the line data at once, I have no way of ensuring the order or correlating those values with what is actually rendered. Perhaps there is some way to manually create a mapping.

You should be able to this with decent performance via gpu picking.

There was a post a while back (GPU picking demo) that demos this (pg) for an instanced mesh.

I’ve modified that pg a bit to use an async version of readPixels here, which improves performance somewhat (dramatically for some mobile/laptop gpus). This works for webgl2 only though, using engine._readPixelsAsync. It will however fallback to synchronous readPixels for webgl1.

You should be able to modify this method to work with the LineSystem - though picking may be a bit hard for 1px wide lines. In the past I’ve dealt with this by widening lines for picking (so pickable area is larger than visible line), but this won’t work for the LineSystem as it uses actual GL.Lines as opposed to triangles. The (fairly) new Greased Lines could be used, or tubes, if wider picking is needed.

2 Likes

Is there not a way to correlate the ‘faceId’ value from the pickInfo object to any information within the LineSystem mesh? Maybe via some mapping of the mesh indices? I feel like as long as the faceIds are deterministic with the input of CreateLineSystem, there has to be some way to accomplish this.

Appreciate the help so far.

I realise this isn’t at all what your last reply asked for, but I ended up making a pg that does gpu picking for a line system, and allows you to specify a pixel buffer around the mouse so each line doesn’t have just a single pixel width to pick. It can also give you back the index into the original line array of the picked line.

Settings are at the top of the pg, currently set to 100k lines with each line having up to 4 vertices.

2 Likes

@sable I appreciate the response and detailed pg! That is above and beyond. Since my renderings are rather simple (just a ton of lines), I think it is overkill to leverage GPU picking and materials as you’ve done here. If the project eventually evolves into something that can benefit this content, I am grateful to have it available. I ended up with just a ‘brute force’ linear solution to find the clicked vertex:

// find vertex closest to pickPoint
let index = 0;
let min = 10;
lines.forEach((line, i) => {
  line.forEach((point, j) => {
    const m = Math.min(min, BABYLON.Vector3.Distance(point, pickedPoint));
    if (m < min) {
      index = i;
      min = m;  
    }
  });
});

// idMap[index] holds my line ID

Elsewhere in the program I create a map to correlate the custom line IDs to the line array passed into CreateLineSystem.

It is an imperfect solution, because the closest vertex does not always fall on the clicked line. Any suggestions for refinement are welcome, but this is performant and accurate enough for now. :slight_smile:

All good - it was a good exercise putting that pg together, and hopefully it’ll prove to be a useful resource for future readers.

Going back to your previous thought of linking faceID to a line in a mesh, it sounds like a great idea, and actually seems to work well - see the below pg:

Should be a bit more performant as don’t need to go back over the vertices calculating distances.

There’s also an intersectionThreshold property on the lineSystem, which can be used to increase picking area. This is in world units though, and will mean that closer (to the camera) lines are picked over those that a visually closer to the picked point (but further back in the scene).

1 Like

Speaking of GPU Picking:
Create a new picking mechanism using GPU by deltakosh · Pull Request #15166 · BabylonJS/Babylon.js (github.com)

2 Likes

Wow. I had something very close to this but couldn’t quite get it working. I thought perhaps the faceId values were not generated in order and that’s why it wasn’t working. Looks like I just needed to use the ‘subMeshFaceId’ field from pickInfo (vs. faceId). TIL. Thanks again for everything!

1 Like