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).

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


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: