[solved] "Tilt Brush" functionality using meshilnes very unperformant in webxr

EDIT: Solved, found a similar issue here and adopted OP’s strategy (set the rest of the array to the most recent controller position every frame. Still not sure how setDrawRange even worked, per that comment. Leaving this here for posterity!)

Working code:

    for (var i = this.currentPos; i < MAX_POINTS * 3; i++) {
      this.positions[i * 3] = position.x;
      this.positions[i * 3 + 1] = position.y;
      this.positions[i * 3 + 2] = position.z;
    }
    this.currentPos++;
    this.line.setBufferArray(this.positions);
  }

Hi everyone,

I’m working with threejs-meshline (Ryan King’s fork of @thespite’s pretty notable lib) trying to create a brush for Tilt Brush-type painting in WebXR. The code is currently working, but performs pretty horribly (in contrast to regular lines, which work fine; however, we need a “brush-like” line width). I’ve been through the weeds of how to approach updating the geometry and have settled on:

  1. Creating an initial Float32Array called this.positions of size (MAX_POINTS *3) (where MAX_POINTS is 100, 1000, 10000 - doesn’t seem to impact perf at those ranges), and a drawCount counter that’ll update every frame and set the range to that counter, basically updating the latest controller position vector in the array and drawing a line to it.

  2. Creating a new MeshLine() object and setGeometrying said array

  3. On Update (/rAF), if controller trigger is pressed, update said array as such:


  DrawLine(position) {

    this.positions[this.drawCount * 3] = position.x;
    this.positions[this.drawCount * 3 + 1] = position.y;
    this.positions[this.drawCount * 3 + 2] = position.z;
    this.drawCount++;

    this.line.setBufferArray(this.positions);
    this.line.setDrawRange(0, this.drawCount);
  }

where setBufferArray looks like this:

  MeshLine.prototype.setBufferArray = function(ba, wcb) {
    this._bufferArray = ba
    this.widthCallback = wcb || this.widthCallback
    this.positions = []
    this.counters = []
    for (var j = 0; j < ba.length; j += 3) {
      var c = j / ba.length
      this.positions.push(ba[j], ba[j + 1], ba[j + 2])
      this.positions.push(ba[j], ba[j + 1], ba[j + 2])
      this.counters.push(c)
      this.counters.push(c)
    }
    this.process()
  }

here’s what it looks like. Unamazing…

As I’d mentioned, this happens extremely slowly on a very good machine (i7, 32GB Ram, 2070RTX…).

I’m wondering what the most optimized way to dynamically create meshlines is, given that I’m already working with Float32Arrays and MeshLines (which inherit from BufferGeometry). I’ve taken a look at the fat lines example but unless I’m wrong, it doesn’t lend itself to dynamic position updating at runtime. I might give it another go, but at this point I’ve been dealing with this long enough to seek help.

The current code is here.

Any/all pointers very welcome. Thank you in advance!

You might find some ideas in the A-Painter performance optimizations.

thanks! I’ve been referencing A-Painter and this document is really helpful to make sense of their treatment of sharing BufferGeometries.

I suspect I’m doing something much dumber than those solutions solve -they seem to be catered towards mitigating massive quantities of tris and meshes, whereas my solution is slow from the first tri. But I’ll dig deeper into that post, thanks!