Like 3 years ago, I tested Three.js, with great joy.
I tried to make a few demos, like a logo for my company, some 3D scenes, etc…
But I quickly struggled to a limitation: after 10K facets, it was unusable because the FPS dropped down to 25 images per seconds.
I’m not sure how many facets Three.js can draw nowadays, perharps it’s the same I think unlesse the engine has been rewritten.
Nevertheless, in those years, I tried to overpass this limitation of the 10K facets.
And I managed to draw more than a million of facets.
Actually, not all facets are drown, only 100K maximum.
But I could still manage worlds up to 10M facets before droppping to 25 FPS.
I never published my work, I had no time to rework the Three.js engine nor engage a long discussion about that.
But nowadays, I see that WebGL is still not very used, and the games I played, there were not so many facets as well.
So, if you’re interested, here is a quick overview of what I did.
Firstly, I completely rethinked the facet indexation. With Three.js I saw that a octree is used. But actually an octree in JS is really slow because the structure itself is really heavy.
So, I started with the quickest way to sort in JS: the Array.sort() function.
But it can only sort numbers, otherwise it’s really slow. So, the first thing I did is choose a main vertex for a facet (the verrtex numer 0), and converted its coordinates to integers, and mixed the bits and X/Y/Z coordinates to obtain a plain 32 bits number, with 10 bits for each X/Y/Z, that’s a world with 1 million coordinate steps in each axis. The facets can be sorted with the plain JS sort function. They can be searched for with dichotomia algorithm, and also merged, substracted, union, intersection. You just need the min/max X/Y/Z coordinates to reduce the time spent in those operations. Also, I used ASM.js tricks to mix X/Y/Z coordinates quickly.
Now, inserting new facets might seem difficult with only that trick. So, I also used an array of those sorted facets. The first slot of the array holds at most 8 facets. Then if the slot is full, the second slot is used and can contain up to 16 facets, and so on until the 20th slot wich can contain 1 million facets. Now I could merge nearly instantly lots of facets together. Inserting 10K facets is just finding the first slot that can fit them, and adding all browsed slots together until a free slot is found, then sorting once the collected facets into one slot. This is a linear algorithm on average. Over 1 mililion facets however, it started to drop below 50 FPS.
Removing the facets, now: I just kept track of the slot sizes, and removed the facets by setting it to null. If the size of the slot drops under half its size, then I create/merge with the lower slot.
Ok that’s fine, but what about WebGL? I had the problem that sending lots facets
to WebGL is slow. So, each time I sort a bunch of facets, I also prepare a WebGl segment and attach it to its slot. When I update facets in the slot, I also change WegGL facets and only those that are needed, removed facets have all 0’s coordinates and are detected by the shaders as deleted facets. So I never need to resize the WebGL segments, only create them, and reuse them (I don’t actually delete the WebGL segment until the number of slots really diminuish).
Also, asking WebGL to draw what’s inside the camera view is easy: just find lower/upper bounds in each slot, and ask WebGL to draw just that part.
And, large objects can be added or removed easily: facets usually stand close together, so they are in the same slot, and merging slots keeps them together. There is always a slot close to the size of any object, so they can be removed/added/updated quickly.
The rest of the functionalities are working too: camera clipping, finding a facet in any way, shaders, colors, etc… But also geometry operations are much faster, especially with lots of facets. Only, the indexation is different, and also the way to pass facets to WebGL (facet segments are kept between frames).
Although, it might not work good with some special worlds, like having 1 million facets in a place and another million in another distant place, because of integer indexation. Other than those particular features, it works very well. The overall number of JS code line was about one thousand for the core indexation algorithms (then I wrote a few thousand more for other features, like the ability to transpile JS shaders to WegGL, shader & object library etc…, and because I couldn’t use Three.js after those changes).
I hope my explanations are clear enough, but if you have any question, I may answer them if I can. I have no time to rewrite this piece of code, but I remember it took me 3 week ends to write it. But then I am not very used to Three.js code base, so I had no time to change Three.js engine.
I hope that someone will be interested enough in drawing 1 million facets at 50 FPS to apply those techniques to Three.js and manage even bigger worlds.