Here’s a snippet from one of my projects that lets you select a bone based on where you clicked a skinned mesh. I’ve added some comments so it’s hopefully more clear.
// extract information from the raycast hit
const { object, geometry, face, point } = hit;
// temp variables
const triangle = new Triangle();
const baryCoord = new Vector3();
const getFunctions = [ 'getX', 'getY', 'getZ', 'getW' ];
const skinWeightAttr = geometry.getAttribute( 'skinWeight' );
const skinIndexAttr = geometry.getAttribute( 'skinIndex' );
const weightTotals = {};
const aIndex = face.a;
const bIndex = face.b;
const cIndex = face.c;
// Get the position of the triangle vertices after being transformed by bones
// and into world space
object.boneTransform( aIndex, triangle.a );
object.boneTransform( bIndex, triangle.b );
object.boneTransform( cIndex, triangle.c );
triangle.a.applyMatrix4( object.matrixWorld );
triangle.b.applyMatrix4( object.matrixWorld );
triangle.c.applyMatrix4( object.matrixWorld );
// Get the Barycoordinate interpolation parameters
triangle.getBarycoord( point, baryCoord );
for ( let i = 0; i < skinIndexAttr.itemSize; i ++ ) {
// get the first set of skin weight info for the vertices
const func = getFunctions[ i ];
const aWeightIndex = skinIndexAttr[ func ]( aIndex );
const bWeightIndex = skinIndexAttr[ func ]( bIndex );
const cWeightIndex = skinIndexAttr[ func ]( cIndex );
const aWeight = skinWeightAttr[ func ]( aIndex );
const bWeight = skinWeightAttr[ func ]( bIndex );
const cWeight = skinWeightAttr[ func ]( cIndex );
// initialize the weights to 0 if they haven't been set yet
weightTotals[ aWeightIndex ] = weightTotals[ aWeightIndex ] || 0;
weightTotals[ bWeightIndex ] = weightTotals[ bWeightIndex ] || 0;
weightTotals[ cWeightIndex ] = weightTotals[ cWeightIndex ] || 0;
// add the interpolated weight for each vertex to the stored total
weightTotals[ aWeightIndex ] += aWeight * baryCoord.x;
weightTotals[ bWeightIndex ] += bWeight * baryCoord.y;
weightTotals[ cWeightIndex ] += cWeight * baryCoord.z;
}
// Sort the weight info by total weight so we can find the highest weighted index
const sorted =
Object
.entries( weightTotals )
.map( ( [ key, value ] ) => ( { weight: value, index: key } ) )
.sort( ( a, b ) => b.weight - a.weight );
// Get the bone and bone index from the click
const boneIndex = sorted[ 0 ].index;
const bone = skeleton.bones[ boneIndex ];