One of the many problems with displaying OpenSimulator content in a web browser is that the WebGL interface doesn’t handle lots of draw operations well.
I have been using the Army Research Lab’s Atropia region OAR files for testing as it includes meshes as well as many shopping scripted objects.
The Atropia regions consist of nine 256x256 meter regions arranged in a 3 by 3 pattern. This table summerizes the basic content of the regions.
test88 | 00 | 01 | 02 | 10 | 11 | 12 | 20 | 21 | 22 | |
---|---|---|---|---|---|---|---|---|---|---|
Simple prims | 19 | 109 | 3088 | 10 | 416 | 9317 | 13 | 2 | 4063 | 0 |
Sculpties | 0 | 1 | 0 | 0 | 187 | 1986 | 0 | 0 | 4175 | 0 |
Mesh assets | 0 | 0 | 2 | 0 | 47 | 213 | 0 | 0 | 0 | 0 |
Of the many approaches to making the scene easier to render, I started with merging meshes that share textures. I decided that I could partition the scene into two types of objects: static and non-static. “Static” objects are those with no scripts and no physics. This should be buildings and plants in the scene. “non-static” objects are those that either have physics enables or have a script that is waiting for touch or collisions.
The initial thought is that all static objects can be merged and have their meshes rearranged in any way while the non-static objects need to be kept as separatable objects. The next table shows a simple partitioning of the regions into these static and non-static objects. In this table, “meshes” is the count of individual meshes after prims have been converted into meshes for rendering (a single prim will convert into 6 or more face meshes).
test88 | 00 | 01 | 02 | 10 | 11 | 12 | 20 | 21 | 22 | |
---|---|---|---|---|---|---|---|---|---|---|
Static meshes | 166 | 928 | 15995 | 44 | 2910 | 55398 | 95 | 15 | 17499 | 1 |
Static materials/textures | 93/4 | 520/30 | 8141/81 | 8/3 | 1209/80 | 27402/637 | 44/11 | 5/3 | 16256/38 | 1/1 |
nonStatic meshes | 0 | 0 | 5799 | 20 | 0 | 4993 | 0 | 0 | 120 | 0 |
nonStatic materials/textures | 0/0 | 0/0 | 2671/37 | 8/3 | 0/0 | 1618/94 | 0/0 | 0/0 | 53/15 | 0/0 |
So, all the static meshes were combined into a set of rebuilt meshes each of which shared similar surface materials (and thus could use the same WebGL draw command). Each of the non-static objects has any common material meshes combined and the resulting numbers were:
test88 | 00 | 01 | 02 | 10 | 11 | 12 | 20 | 21 | 22 | |
---|---|---|---|---|---|---|---|---|---|---|
Rebuilt static meshes | 12 | 128 | 801 | 7 | 375 | 4014 | 32 | 3 | 211 | 1 |
Rebuilt nonStatic meshes | 0 | 0 | 654 | 6 | 0 | 857 | 0 | 0 | 33 | 0 |
Total WebGL draws | 12 | 128 | 1055 | 13 | 375 | 4868 | 32 | 3 | 244 | 1 |
draws before rebuild | 166 | 928 | 21794 | 64 | 2910 | 60391 | 95 | 15 | 17619 | 1 |
As you can see from the last rows, the rebuilding and merging of common surface materials significantly reduced the number of potential WebGL draw commands.
Merging common materials gets the number of draws within usability, there are still some problems:
- even at 1000 draws, atropia02 still crashes ThreeJS and BabylonJS with out of memory errors;
- materials with transparency (most commonly the plant sculpties) cannot be merged as most 3D renderers will Z-order transparent meshes but do not depth order the individual triangles within a mesh;
The above scene rebuild is mesh-centric. That is, the meshes are merged without thought as to what they belonged to (other than static and non-static). Since these scenes have many duplicated meshes (the bushes scattered around the scenes), another approach would be to use mesh instancing to reduce draws. This technique will be explored next.