How to scale Vertices of a Model in React Three Fiber?

I have a model that will be exported as gltf and imported to my React Three Fiber scene. I want to be able to change the shape of the model by scaling a section of it, so it’s not a uniform scale on the entire model. I made a screen recording of the scaling effect I want to achieve. I was thinking maybe I need to look into morphtargets but I felt that would be limiting the size that user’s can achieve. Are morphtargets really the way to go or is there a simpler way to scaling a section of a model?

If you want to replicate what 3D editing software does - you have to write your app as if it was a 3D editing software. Ie. you shouldn’t look at this as scaling a mesh / group - rather as displacing and modifying the position buffer (which contains the vertices) directly.

With exception of features like blender modifiers - 3D apps don’t store user operations as a chain of matrix transformations of translation / rotation / scale, they just move the vertices directly. And to do that easily - its likely best all your meshes have a scale of 1/1/1 at all times.

Great okay I am pretty close to what I want however I don’t know the equation to get all the vertices to move outwardly like how you saw in my first screen recording where I scaled the vertices in the 3d App. Currently they all move in one direction but not outwardly respective to their positions. I tried adding 1 to each vertex and multiplying by 1.1. Each gave a similar result (all moving in a direction). The relevant code.

onClick={(e) => {                
                const positionAttribute = nodes.table_A_Base_1.geometry.attributes.position;
                const positions = positionAttribute.array;

                for (let i = 0; i < positions.length; i += 3) {
                  const x = positions[i]; // Get the X coordinate of the vertex
                  const y = positions[i + 1]; // Get the Y coordinate of the vertex
                  const z = positions[i + 2]; // Get the Z coordinate of the vertex

                  if (y < 2) { 
                    positions[i] *= 1.1; // Scale the X coordinate 
                    positions[i + 2] *= 1.1; // Scale the Z coordinate
                  }
              }
                positionAttribute.needsUpdate = true;
                nodes.table_A_Base_1.geometry.computeBoundingSphere();
}

Ok after more tinkering around (chatgpt :wink: ) I was able to get the effect I wanted. All I had to do was also factor in the vertexes Normals to the increments.

<mesh
              onClick={(e) => {
                
                const positionAttribute = nodes.table_A_Base_1.geometry.attributes.position;
                const normalAttribute = nodes.table_A_Base_1.geometry.attributes.normal;
                const positions = positionAttribute.array;
                const normals = normalAttribute.array;

                const moveDistance = 0.1; // Distance to move along the normal (adjust as needed)

                for (let i = 0; i < positions.length; i += 3) {
                  const x = positions[i]; // Get the X coordinate of the vertex
                  const y = positions[i + 1]; // Get the Y coordinate of the vertex
                  const z = positions[i + 2]; // Get the Z coordinate of the vertex               
                  
                  const nx = normals[i];
                  const ny = normals[i + 1];
                  const nz = normals[i + 2];

                   
                  if (y < 2) { 
                    positions[i] += nx * moveDistance;
                    // positions[i + 1] += ny * moveDistance;
                    positions[i + 2] += nz * moveDistance;
                  }
              }
                positionAttribute.needsUpdate = true;
                nodes.table_A_Base_1.geometry.computeBoundingSphere();
}>

that’s pretty incredible if chatgpt solved that for you.

Lol yeah it’s true, in fact let me put the exact prompt and the answer chat gpt gave me here, verbatim.

ME:
OK now how can I move my vertices relative to their normals direction?

CHATGPT:
To move vertices along their normals, you need to follow these steps:

  1. Retrieve the positions and normals of each vertex.
  2. Scale the normal vectors by a certain factor to determine the direction and distance of movement.
  3. Add the scaled normal vectors to the vertex positions.

Here’s how you can achieve this in Three.js:

const positionAttribute = nodes.table_A_Head_1.geometry.attributes.position;
const normalAttribute = nodes.table_A_Head_1.geometry.attributes.normal;
const positions = positionAttribute.array;
const normals = normalAttribute.array;

const moveDistance = 0.1; // Distance to move along the normal (adjust as needed)

for (let i = 0; i < positions.length; i += 3) {
    const x = positions[i];
    const y = positions[i + 1];
    const z = positions[i + 2];

    const nx = normals[i];
    const ny = normals[i + 1];
    const nz = normals[i + 2];

    // Move the vertex along its normal
    positions[i] += nx * moveDistance;
    positions[i + 1] += ny * moveDistance;
    positions[i + 2] += nz * moveDistance;
}

// Update the buffer attribute array with the modified values
positionAttribute.needsUpdate = true;

// Notify Three.js that geometry has changed
nodes.table_A_Head_1.geometry.computeBoundingSphere(); // Update bounding sphere (optional)

In this code:

  • We iterate through each vertex in the geometry.
  • For each vertex, we retrieve its position (x, y, z) and normal vector (nx, ny, nz).
  • We move the vertex along its normal vector by scaling the normal vector by the desired move distance and adding the result to the vertex position.
  • Finally, we update the buffer attribute array with the modified vertex positions and notify Three.js that the geometry has changed.

Adjust the moveDistance variable to control how far the vertices move along their normals. Positive values move the vertices outward from the surface, while negative values move them inward.