How to update BufferGeometry vertices as a track on a circle

I’m trying to build a 3d model of an arm, and making it do some poses. I separate it into upperarm and forearm. The upperarm is at position 0,0,0. and let its length equal to y1, the forearm is at postiion 0,y1,0. so that the two parts look like connectted.

When I rotate the forearm, I update some of the vertices of the BufferGeometry in the upperarm, make sure it is still connected with the forearm. This is easy to find, because I can read the positions in the forearm BufferGeometry, and translate it to world position and further more the positions in the upperarm local object.

The problem is that, I can only know the positions of the vertices that is overlapping with the forearm, if I want to update the vertices in the forearm like how a real elbow moves, I got stuck.

If describe it as a Math problem, it’s like I have two lines, A (forearm), B (upperarm) and they are joined by a curve E (elbow), when A rotates, the E should become a curve of different radius and length, I don’t know how to do that with updating vertices of BufferGeometry.

A straight arm
2022-11-16_174423

Forearm rotated a small angel
2022-11-16_174539

Forearm rotated a large angel
2022-11-16_174439

Any suggection are appreciated, maybe an entire different approach, as long as it can solve the problem.

For a real arm this is not trivial, because the skin stretches and also forms wrinkles. To achieve a realistic representation, one would have to measure the arm in some intermediate positions and store the data and then set and connect certain vertices accordingly.

I have only created some primitive joints.

See from the Collection of examples from discourse.threejs.org :
DynamicTubeGeometryCaps
Pino
CustomCylinderAndKnee
CustomCylinderAndSphereKnee
BendCylinderToKnee
BumblebeeMara

I think I don’t need it to be too realistic, like forming wrinkles, something like the model in the Ring Fit Adventure is good enough for me.
Those are very interesting examples, expecially the DynamicTubeGeometryCaps. Any chance I can learn the code/math of those examples?

I myself do not deal with modeling software, but I know from the posts here in the forum that such figures are better modeled and animated with such software.

Check out the text and video on the UPDATE Feb 19 of the post Skinned mesh with dynamic knees. The problem is the torsion not only to one axis.


The mathematics of
https://hofk.de/main/discourse.threejs/2022/DynamicTubeGeometry/DynamicTubeGeometryCaps.html
regarding the space curve is already integrated in three.js : three.js/CatmullRomCurve3.js at f021ec0c9051eb11d110b0c2b93305bffd0942e0 · mrdoob/three.js · GitHub

Around this curve I form orthogonal circles in space. Of course you can experiment and use different radii over the curve.

g.pts = new THREE.CatmullRomCurve3( points , false ).getSpacedPoints( g.heightSegments ); // new center points
     
// tangent( direction),  normal, binormal, shape in space

let v3a = new THREE.Vector3( ); 
let v3b = new THREE.Vector3( );

let tangent = new THREE.Vector3( );	
let normal = new THREE.Vector3( 0, 0, -1 ); // first normal to after ... 
let binormal = new THREE.Vector3( );

let idx = 0;

for( let i = 0; i <= g.heightSegments; i ++ ) {
    
    if ( i === 0 ) tangent.subVectors( g.pts[ 1 ], g.pts[ 0 ] );
    if ( i > 0 && i < g.heightSegments ) tangent.subVectors( g.pts[ i + 1 ], g.pts[ i - 1 ] );
    if ( i === g.heightSegments ) tangent.subVectors( g.pts[ i ], g.pts[ i - 1 ] );
    
    binormal.crossVectors( normal, tangent );
    normal.crossVectors( tangent, binormal );
    
    binormal.normalize( );
    normal.normalize( );
        
    for( let j = 0; j <= g.radialSegments; j ++ ) {
    
        // circle in space
        v3a.addVectors( binormal.clone( ).multiplyScalar( Math.sin( Math.PI * 2 * j / g.radialSegments ) ), normal.clone( ).multiplyScalar(  Math.cos( Math.PI * 2 * j / g.radialSegments ) ) );
        
        v3a.multiplyScalar( g.radius );
        
        v3b.addVectors( g.pts[ i ], v3a );
        
        g.attributes.position.setXYZ( idx ++, v3b.x, v3b.y, v3b.z );
        
    }
    
}

The idea is quite simple. From the tangent you calculate the normal and binormal. With this you have a system for the plane in space on which the circle lies.

Now you add to the vector of the center of the circle simply the radius vector for all angles of the circle according to the number of segments.

You get the radius vector by sin and cos in connection with normal and binormal.

In the formula above, this is nested and therefore difficult to see.

// vector equation of circle in 3D space (component notation)
// c center, b binormal, n normal

x = cX + r * ( Math.cos( angle ) * bX + Math.sin( angle ) * nX );
y = cY + r * ( Math.cos( angle ) * bY + Math.sin( angle ) * nY );
z = cZ + r * ( Math.cos( angle ) * bZ + Math.sin( angle ) * nZ );

This is how I used it in my THREEf.js addon. THREEf.js/THREEf.js at 434de5be15e741b0dc3db29cb3fcd63f00d22809 · hofk/THREEf.js · GitHub line 1828 See Addon. Produces almost infinite many time-varying geometries with functions


You can find another variant there in the source code: https://hofk.de/main/discourse.threejs/2021/CirclesOnCurve/CirclesOnCurve.html


Sources
15-462 Graphics Project 2: Simulating a Roller Coaster via Splines - Camera Control Method
http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts

2 Likes

@hofk Thanks for the detailed info, really helpful