[Solved] SkinnedMesh - Bind Vertex to Closest Bone

I use OpenCV to process any given image (segment, convex hull, cavity). Then I use Three.js to use these coordinates as bones. To create a SkinnedMesh, I started with this cylinder example, which is 1-linear. Since my goal is to support arms/legs, I decided to get distance from vertex to closest bone, which resulted in many failures.

The vertices at the skinIndex don’t respond. The shadow moves with the animation, but not the model. The sides move, but not the front/back. This seems like a timing issue, which may require a promise/async(?) I have tried calling needsUpdate, assigning a count, setting dynamic to true, etc.

I realize this is unfinished homework, but is something fundamentally wrong?

https://codepen.io/kpachinger/pen/ZVgejg

var skinIndices = [];
var skinWeights = [];

function distance(x1, y1, x2, y2) {
  let a = x1 - x2;
  let b = y1 - y2;
  let c = 1 - Math.sqrt(a * a + b * b);
  c = Math.abs(c);

  return c;
}

let pos = geometry.attributes.position;
var promise = new Promise(function(resolve, reject) {

  let root = sto.skind[0].id;
  let skin = [];

  for (let i = 0; i < pos.count; i++) {
    x1 = pos.array[i * 2];
    y1 = pos.array[i * 3];

    skin[i] = [];

    for (let j = 0; j < bones.length; j++) {
      x2 = bones[j].positionGlobal.x / sto.width;
      y2 = bones[j].positionGlobal.y / sto.height;

      if (bones[j].name != 'Root' && bones[j].name != 'Bone_0') {
        skin[i].push(distance(x1, y1, x2, y2) + '|' + bones[j].id);
      }
    }

    skin[i].sort();

    let closest = skin[i][0];
    closest = closest.split('|');
    closest = {
      'dist': closest[0],
      'id': closest[1] * 1
    };

    const halfHeight = area / (sto.width + sto.height) / 4;
    const segmentHeight = halfHeight / 3;

    const y = Math.abs(y1);
    var skinIndexY = Math.floor(y / segmentHeight / 2);
    var skinWeightY = ((y / segmentHeight) * (halfHeight / 2)) / 2;

    skinIndices.push(
      skinIndexY,
      closest.id,
      closest.id,
      root);

    //all add up to 1
    skinWeights.push(
      0.5 - (skinWeightY*0.50),
      (skinWeightY*0.50),
      0.25,
      0.25);

  }

  resolve('Vertices => SkinnedMesh');
});


promise.then(function(value) {
  console.log(value);

  geometry.addAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices, 4));
  geometry.addAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeights, 4));

  mesh = new THREE.SkinnedMesh(geometry, [material, materialSides]);
  let skeleton = new THREE.Skeleton(bones);

  mesh.add(bones[0]);
  mesh.bind(skeleton);

  let skeletonHelper = new THREE.SkeletonHelper(mesh);
  skeletonHelper.material.linewidth = 2;

  group.add(mesh, skeletonHelper);

  return mesh;
});

SOLVED: the fix(es):

  • var vertex = new THREE.Vector3();
  • vertex.fromBufferAttribute(position, i);
  • skinIndices array should increment new, unique bone id’s, as opposed to reference pre-existing.
  • don’t mix SkinnedMesh and Mesh within texture, or else tears/disappears/un-sync shadow