3

We are trying to build "edit handles" that allow the user to resize a (projected) plane by dragging at one of 8 places (4 corners + 4 edges). This resizing is supposed to only scale/stretch the surface in the direction of the dragging, so the default scale mechanism of three.js won't do, as it scales in two opposite directions at the same time like seen in this example.

I've read and tried a lot, especially these two posts:

Following those I ended up with this

// preparing to resize into -x direction aka "left edge"
// moving the geometry center to the right edge
mesh.geometry.translate(width/2, 0, 0);
mesh.geometry.scale(scaleX, 1, 1);
// moving the geometry center back to the middle 
// as more resizing might happen, into another direction, as per comment here: 
// https://stackoverflow.com/questions/26817717#comment74469004_26818522
mesh.geometry.translate(-(width/2 * scaleX), 0, 0);

The result is not what I would expect: The scaling happens in the two opposite directions again, so the translations cancel each other out - so either the scaling does not happen while the center/point of origin is placed on the right edge or there is a misconception on my end.

This on the other hand works fine

mesh.scale.set(scaleX, 1, 1);
mesh.position.x = (-width / 2);

But it really feels hacky to reset the position of the mesh over and over again - also I thought I have read that scaling the mesh is less performant as this transformation is reapplied for every frame (might have gotten this wrong though: Three.js - Scale model with scale.set() or increase model size?), while a transformation of the underlying geometry is baked into the data.

I also had a look at the sources of the TransformControls, but as it doesn't work the way we want and as I haven't fully grasped the concept of quaternions its lost on me.

So my question is this: Is there a way to do inplace unidirectional resizing without correcting the position of the mesh?

Benjamin Seiller
  • 2,875
  • 2
  • 32
  • 42

1 Answers1

3

As you've discovered, you have two ways of approaching this problem.

  1. Scale the geometry.
  2. Scale the Mesh and adjust its center position.

First, let's discuss option 1, which I am NOT recommending, for reasons I'll make clear later.

To scale geometry linearly in a single direction, you still need to move ALL of the vertices in the direction of the scaling. So for each vertex, you'd need to calculate its new position based on the amount of scaling that vertex would receive (vertices closest to the scaling side move the most, vertices in the middle move half as much, and vertices on the opposite side of the scaling hardly move at all). If you're doing live scaling, that is potentially A LOT of calculations.

Now consider option 2: You change the matrix for the entire mesh (this includes its new position). That's it. The matrix is sent to the GPU, which handles the rest. As I'm sure you know, the GPU is VERY good at math (including matrix math), and JavaScript is...less good at it, or at least much slower. Performing the matrix manipulations is nothing to the GPU.

Not to mention, with the transformation matrix in place, you can always get the world positions of the vertices:

vertexCopy.set(myMesh.geometry.attributes.position.getX(index),
    myMesh.geometry.attributes.position.getY(index).
    myMesh.geometry.attributes.position.getZ(index));
myMesh.localToWorld(vertexCopy);

Synopsis:

If anything, manipulating the geometry is more "hacky" than manipulating the transformation matrix. It's like you're telling the system how to draw each pixel of a square, when you could simply tell it to draw a square.

  1. Updating the geometry is computationally expensive. Even though the vertices are essentially "baked" afterward, the next manipulation will trigger more computations.

  2. Updating the mesh's matrix is simple, and offloads the computational complexity to the GPU, which will have no problems processing that information.

You're welcome to pursue whichever method fits your requirements, but from the information you've given, I very much recommend option 2.

A third idea:

You could potentially use Morph Targets to achieve your desired effect, but I don't have much experience with them. Here's an example of cube being deformed by morphing. You'd need to affect multiple morph targets to achieve the effect you're seeking, but it's still probably better than manipulating the geometry, because the morph manipulations would be done on the GPU.

TheJim01
  • 8,411
  • 1
  • 30
  • 54
  • Ok, so just to prevent confusion - is it intentionally that the options in the end are in opposite order than those at the beginning? – Benjamin Seiller Aug 03 '17 at 21:50
  • _Couldn't change the comment after writing it..._ First of all, thank you very much for elaborated & detailed answer. So just to prevent confusion: Is it intentionally that the options in the end are in the opposite order than those at the beginning? _... (see above)_ And to follow up on this, can you point me to an example how to adjust the center position of the mesh? I didn't know that is possible as well. Unless you mean changing the position - with `.translate` and the likes. – Benjamin Seiller Aug 03 '17 at 21:58
  • **1)** Sorry, I've adjusted the order of the options for clarity. Blame a long day. :) **2)** Yes, I only mean to translate the mesh to compensate for the new scale. **3)** I'll make another edit in a moment... – TheJim01 Aug 03 '17 at 23:19