Performance of scene traversal

Hey guys!

I’ve been working on a BIM-model viewer in my off hours that renders IFC (step) files for quite a while and stumbled across a bottleneck with various ways of (maybe?) fixing it. I was wondering if any of you have some ideas how to go about this.

The geometry I’m working with are really simple; mostly just boxes, some cylinders, etc. The problem is that there are a lot. Most of them do share the same material, but I’m not overstating if I say there can be a few thousand separate Object3D (or Mesh) instances within a single scene.

If I merge everything together in one object (per material) there are no notable performance issues whatsoever. If every object is separate however, the performance starts to drop when objects are visible on camera (occluded or not, it doesn’t matter).

My gut feeling says it has to do with the amount of objects the renderer has to traverse through every single frame. Correct me if I’m wrong please :sweat_smile: The objects can’t be merged together because they have to be individually selectable and contain a lot of data (wind/fire resistance, weight, pressure, measurements, materials, quantities, etc. etc.)

Does any of you have any idea on a technique that could be used here to keep the performance optimal, while keeping the functionality of selecting individual objects? To make matters (a little) worse, I’m also creating clones of every piece of geometry and running them through EdgesGeometry to create LDraw-style outlines.

Example result of a 500mb model that can’t be JSON.stringified because the string length exteeds the capabilities of the v8-engine to give you an idea:

The interiors of these models are detailed nearly to the individual screw. This information is important and can’t be lost.

Just to clarify, I’m not looking for a very specific detailed answer (unless you feel like giving one :stuck_out_tongue_winking_eye: ). If you know of a technique I could apply to this, that would be very helpful!

Thanks in advance!

P.S.: Since the average model is (way) too large to process in a browser, I’m offloading model conversion on a NodeJS server and had to build my own hand-crafted file type. This gives me the flexibility to store everything in binary format due to memory limitations of v8 (javascript) in general. The upside is that the user doesn’t have to wait for a blocking process in the browser, so it isn’t a big deal if the conversion process takes a few minutes (it converts this model shown in the screenshot in about 2 minutes on my PC (i7 8700k), the source file is about 550mb in size, the output is about 40megs due to it being in compressed and binary format).

1 Like

each mesh will cause a draw call, more draw calls mean more overhead. if you have events/raycasting it traverses all suitable meshes each frame. the edges are probably the most expensive in your scene.

  • re-using materials is good, you could go as far as using a single texture that contains all the colors
  • you should instance every repeating object to save draw calls, for instance windows, screws, etc. you can still select them
  • if your scene really just consist of boxes you could instance everything into a single draw call (!) these meshes can still have distinct colors, sizes and can be selectable, click
  • only raycast objects that are interactive, do not traverse the model
  • something like three-mesh-bvh would speed up selection
  • or change the raycast to something cheap like a bounds-only check
  • clamp off some of the resolution, instead of 2 use 1.5, it’s sharp enough
  • objects in the distance could be wrapped into a LOD
  • find a way to occlude objects if they’re not visible (inside/outside), this could be as naive as a button that transports the user inside
  • you could merge everything into one buffergeometry and use temporary mesh overlays for selections that you mount into the scene, we use that at work as well (cad system, very intense models)
  • think about removing edges, it will look like hell but you can counteract that with shadows + shadowMap.autoUpdate = false and/or a low res ssao effect
  • movement regression, for instance sink resolution to 1 while the camera is spinning

some of these i’ve collected here with examples, you could pick up some ideas or impressions there

3 Likes

This is like saying, I have $10 but I need to buy $100 worth of goods.

Not only it can be lost, but it has to be.

add a vertex attribute that will store an original object id, to a merged object

  • you could merge everything into one buffergeometry and use temporary mesh overlays for selections that you mount into the scene, we use that at work as well (cad system, very intense models)

This sounds like a good idea for my use case. Thanks! :smile:

Hi, do you have any progress on this? I have the same situation and am stuck with pure performance issues. I use Blender to convert IFC to GLB, and the size is only 5MB, yet the performance is still poor
schoolOri.glb (5.5 MB)

There are about 6700 meshes in this scene, it’s too many draw calls, and a large scene graph to traverse. Probably best to start a new thread if you need help tailored to this particular model, but any method of merging meshes and reducing draw calls would help, but the exact approach depends on how/if you need to interact with distinct meshes in your application. A simpler approach to merge everything would be:

gltf-transform optimize schoolOri.glb schoolOri_optimized.glb --no-instance --no-palette

Thanks for the reply, I really appreciate it.
I did some research and found that the number of objects is the main issue. My goal is to load an IFC model into Three.js while keeping all its information. Blender is the best tool I know for converting it to GLB. I tried all gltf-transform options I could, but any join or instance command results in the model losing information. I wonder if there’s another approach or technique since the IFC model is really large (20,000+ meshes).

Assuming the information “lost” is the distinct meshes themselves, and that the quantity of meshes is also the cause of the performance issue, you probably need to figure another way to represent things such that interactive objects do not correspond 1:1 to GPU draw calls (that is, batching). Converting the scene into one or more BatchedMesh objects for example. I’ve written a bit more about how that would relate to CAD and AEC models in:

1 Like

You don’t need the entities to be the same, ie the thing you are rendering can be different from the thing you are say, intersecting. You can have 20k meshes with all of your information, and have one mesh drawing all of them.

1 Like