0
votes

I'm implementing an interface for resizing a cube by clicking + dragging its faces.

I'd like to implement this by updating the position attribute on the buffer geometry object, and either recreate the mesh or just set the needsUpdate flag and let it update itself. Neither of these options have worked for me. My latest attempt is below.

  this.boxGeo = new THREE.BoxGeometry(2,2,2)
  this.boxMesh = new THREE.Mesh(this.boxGeo, this.boxMaterial)    

...

  // let disp = the amount we want to move the face

  // vertex indices
  let indices = this.planes[index].indices

  // new typed array for position attribute
  let positions = new Float32Array(8 * 3)

  // for each of the 8 verts
  for (let i=0; i < 8; i++) {
    if(!indices.includes(i) || disp === 0) {
      positions[i * 3]     = this.boxGeo.vertices[i].x
      positions[i * 3 + 1] = this.boxGeo.vertices[i].y
      positions[i * 3 + 2] = this.boxGeo.vertices[i].z
    } else {
      // modify verts
      let d = new THREE.Vector3(disp, disp, disp).multiply(plane.normal)
      positions[i * 3]     = this.boxGeo.vertices[i].x + d.x
      positions[i * 3 + 1] = this.boxGeo.vertices[i].y + d.y
      positions[i * 3 + 2] = this.boxGeo.vertices[i].z + d.z
    } 
  }
  // update geometry
  this.boxMesh.geometry._bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))

I've tried a few other methods including more closely following the documentation here: https://threejs.org/docs/#manual/en/introduction/How-to-update-things

Any help or advice would be much appreciated!

EDIT: Per comments below, I'm taking a closer look at my ...attribute.position.array and noticing that it looks like each face lists all its vertices so I can't access them (or set them) as above. Any advice on docs I should go read? Is there an easier way to do this?

So per @Murgen87's comment the code below works to update the position attribute. It looks like the BoxGeometry primitive does not use indexed faces though, and now I'm thinking it might just be easier to scale / translate the box.

let positions = 
      this.boxMesh.geometry._bufferGeometry.getAttribute('position')

 // this loop doesn't pick the right positions for my use case 
 faces.map((f, i) => {
    positions.array[f * 6 + i * 3]     += displacement.x
    positions.array[f * 6 + i * 3 + 1] += displacement.y
    positions.array[f * 6 + i * 3 + 1] += displacement.z
  })

  positions.needsUpdate = true;

My last remaining question would be why can't I do:

box.geometry.vertices.multiply(displacement)
box.geometry.verticesNeedsUpdate = true

... And this just led me to answer my own question!

1
Please do not replace existing buffer attributes. Update the values instead and then set needsUpdate to true. Also notice that it is not supported to change the size of a buffer attribute. If this is required, you have to dispose the entire geometry and create a new one.Mugen87
Hi @Mugen87 I'm not looking to change the size of the buffer attribute. Can I just reach into this.boxMesh.geometry._bufferGeometry.attributes.position[] and change the values using the loop in the code I posted? I also just noticed that the BoxGeometry has a vertices array and a verticesNeedsUpdate flag, although I didn't see documentation on that.Nick Gimbal

1 Answers

0
votes

The easiest way to do this is:

  this.boxMesh.geometry.vertices.map((v,i) => {
    if(!planeObj.indices.includes(i)) return
    this.boxMesh.geometry.vertices[i].add(displacement)
  })
  this.boxMesh.geometry.verticesNeedUpdate = true

A valid pattern for updating the position attribute is:

  let positions = 
      this.boxMesh.geometry._bufferGeometry.getAttribute('position')
  planeObj.faces.map((f, i) => {
    positions.array[f * 6 + i * 3]     += displacement.x
    positions.array[f * 6 + i * 3 + 1] += displacement.y
    positions.array[f * 6 + i * 3 + 1] += displacement.z
  })
  positions.needsUpdate = true

Note that the loop above does not select the proper elements in the positions.array if just shows how to update if that's what you need to do.

Thanks!