Detect certain gltf model part with Raycaster

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 ];
2 Likes