I would like to display a circuit using react-three-fiber (or with the Drei library).
segments
and turns
.I'd like my user to be able to construct the circuit by himself. It means that he will be able to modify (drag/drop/delete and so on...) the turns and the segments. It will result with around 10,000 of segments and turns and so in more than 100,000 points.
How would you proceed? (see the below for more details about my tests)
r3f
((react-three-fiber) instanceMesh component (with a rectangle): I would need to store a map of the instanceId linked to my turn/segments ids, it does not seem impossible. It seems to be a good solution and is still performant with more than 100k elements. I implemented a quick example here to test this logic.
drei
Instance component: With more than 1k elements, it starts to be laggy. You can play with an example here (adapted from an r3f example)
r3f
instanceMesh component: The turn can be seen as a list of segments. With this solution, if we zoom too much, we can clearly see each segments. It is maybe not ideal, but it seems to be working. Another issue is that a turn can be composed of 100+points. If I have 1k turns, it will draw 100k segments, which starts being a lot
line
: We could create only one geometry with all the turns inside. However, currently all my lines are linked (like if I was drawing them with a pen in one shot). Moreover, I do not think it is easily possible to manipulate a specific turn with this solution.
Line
v2: We could also create as much geometries as turns. Each turn would be an entity instead of lots of segments. However, having 1K+ geometries is not recommended. With this solution, the performances were not good at all.
Limiting the number of objects: Another solution I would like to try is to draw all my turns in one geometry. In this case I would have no interaction. When zoomed enough, with only a maximum of 50 visible, I add real line with all the interactions. It is a bit tricky, but it also seems to be a good compromise.
The ideal would be to have an instance of the turns (such as the segments). However, each turn seems to be unique and I do not see how it would be possible to create only one geometry in an instance to display all the possible turns.
What would be the best way to implement this logic?
I tried this method by drawing only the closest turns. It stays quite fluid. I will try to go further with this solution: Here is a GIF example of the implementation:
I made a PR for a LineSegments
component, and the component is now available in Drei 9.53.0. You can use it like <Line segments />
.
This component can render lines (with gaps) and have a line width (other than 1px) in one call. That would probably be the most performant way of drawing it.
The LineSegments
is basically Drei's Line
component, but using Three.js LineSegments2
instead of Line2
. LineSegments2
or Line2
is what enables us to draw lines with a line width less than one pixel.
For interaction, you can still use the raycaster. The pointer events of R3F also exposed a faceIndex
which you can use to determine which line.
If you want to draw various width, group your lines based on width, and create a LineSegments2
per width.
If the frame rate is too low, combine it with LOD and frustum culling.
Finally, your frame rate can also drop if the number of rerenders is high. Do add some console logs, for example, to see if you are rerendering a lot on the Canvas, but also outside of the Canvas! I've noticed that when I also have a high number of CPU cycles—not related to Three.js—rerender, calculation, etc., the frame rate also drops.