Here’s an attempt for further improvement on this: the weights are assigned by proximity of bones to vertices.
Code
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 3, 40, BABYLON.Vector3.Zero(), scene);
// camera.attachControl(canvas, false);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
const segmentHeight = 2;
const segmentCount = 6;
const height = segmentHeight * segmentCount;
const halfHeight = height * 0.5;
const sizing = {
segmentHeight,
segmentCount,
height,
halfHeight
}
// make skeleton
const skeleton = new BABYLON.Skeleton("skel", "skelid", scene)
// is this line needed?
// skeleton.dimensionsAtRest = BABYLON.Vector3.FromArray([0,10,0])
const im = BABYLON.Matrix.Translation(0, 2, 0); //x,y,z
var bones = [];
bones[0] = new BABYLON.Bone("b0", skeleton, null, BABYLON.Matrix.Translation(0, -5, 0))
for (var i=1; i<segmentCount-1; i++) {
bones[i] = new BABYLON.Bone("b"+i, skeleton, bones[i-1], im)
}
// setting length on the bones doesn't seem to make a difference
// ba.length = 2;
// bb.length = 2;
// bc.length = 2;
// bd.length = 2;
// be.length = 2;
const mesh = BABYLON.MeshBuilder.CreateCylinder("mesh", {
height: height,
diameterTop: 3,
diameterBottom: 3,
subdivisions: segmentCount,
tessellation: 4,
}, scene);
mesh.rotation.y = BABYLON.Angle.FromDegrees(45).radians()
let vertex = BABYLON.Vector3.Zero();
const vertexData = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
const indicesMatrix = []
const weightMatrix = []
const totalVertexCount = mesh.getTotalVertices();
for (let i = 0; i < totalVertexCount; i++) {
BABYLON.Vector3.FromArrayToRef(vertexData, 3 * i, vertex);
//const y = (vertex.y + sizing.halfHeight)/segmentCount*(segmentCount-1);
let d;
let indx = [0,0,0,0];
for (let j=0; j<bones.length; j++) {
const l = bones[j].getAbsolutePosition().subtract(vertex).length();
if (j == 0 || l < d) {
d = l;
indx[3] = indx[2];
indx[2] = indx[1];
indx[1] = indx[0];
indx[0] = j;
}
}
console.log(i + ' ' + indx.join(','));
indicesMatrix.push(indx[0], indx[1], indx[2], indx[3]);
weightMatrix.push(0.8, 0.16, 0.032, 0.0064);
}
mesh.setVerticesData(BABYLON.VertexBuffer.MatricesIndicesKind, new Float32Array(indicesMatrix));
mesh.setVerticesData(BABYLON.VertexBuffer.MatricesWeightsKind, new Float32Array(weightMatrix));
window.mesh = mesh;
mesh.skeleton = skeleton;
var t = 0;
scene.registerBeforeRender(function () {
/*skeleton.bones[0].rotate(BABYLON.Axis.Z, Math.sin(t/1000), BABYLON.Space.WORLD, mesh);
skeleton.bones[1].rotate(BABYLON.Axis.Z, Math.sin(t/1000), BABYLON.Space.WORLD, mesh);
skeleton.bones[2].rotate(BABYLON.Axis.Z, Math.sin(t/1000), BABYLON.Space.WORLD, mesh);*/
for (let i = 0; i < skeleton.bones.length; i++) {
skeleton.bones[i].setAxisAngle(BABYLON.Axis.Z, Math.sin(t/100)/2);
}
t++;
});
// this is an export from blender just to compare
const data = {
"producer": { "name": "Blender", "version": "3.5.1", "exporter_version": "3.3.1", "file": "boxwithbones.babylon" },
"autoClear": true, "clearColor": [0.0509, 0.0509, 0.0509], "gravity": [0, -9.81, 0],
"skeletons": [{
"name": "Armature", "id": 0, "dimensionsAtRest": [0, 10, 0], "bones": [
{ "name": "a", "index": 0, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, -5, 0, 1], "rest": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, -5, 0, 1], "parentBoneIndex": -1, "length": 2 },
{ "name": "b", "index": 1, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "rest": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "parentBoneIndex": 0, "length": 2 },
{ "name": "c", "index": 2, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "rest": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "parentBoneIndex": 1, "length": 2 },
{ "name": "d", "index": 3, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "rest": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "parentBoneIndex": 2, "length": 2 },
{ "name": "e", "index": 4, "matrix": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "rest": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1], "parentBoneIndex": 3, "length": 2 }]
}],
"meshes": [{
"name": "Cube", "id": "Cube", "billboardMode": 0, "position": [0, 5, 0], "rotation": [0, 0, 0], "scaling": [1, 1, 1], "isVisible": true, "isEnabled": true, "pickable": false, "skeletonId": 0, "numBoneInfluencers": 3
, "positions": [-1, 6, -1, -1, 4, 1, -1, 4, -1, -1, 6, 1, 1, 4, 1, -1, 4, 1, 1, 6, 1, 1, 4, -1, 1, 4, 1, 1, 6, -1, -1, 4, -1, 1, 4, -1, 1, -6, 1, -1, -6, -1, -1, -6, 1, -1, 6, 1, 1, 6, -1
, 1, 6, 1, 1, -4, -1, -1, -6, -1, 1, -6, -1, 1, -2, -1, -1, -4, -1, 1, 0, -1, -1, -2, -1, 1, 2, -1, -1, 0, -1, 1, 4, -1, -1, 2, -1, 1, -4, 1, 1, -6, -1, 1, -6, 1, 1, -2, 1, 1, -4, -1
, 1, 0, 1, 1, -2, -1, 1, 2, 1, 1, 0, -1, 1, 4, 1, 1, 2, -1, -1, -4, 1, 1, -6, 1, -1, -6, 1, -1, -2, 1, 1, -4, 1, -1, 0, 1, 1, -2, 1, -1, 2, 1, 1, 0, 1, -1, 4, 1, 1, 2, 1
, -1, -4, -1, -1, -6, 1, -1, -6, -1, -1, -2, -1, -1, -4, 1, -1, -4, -1, -1, 0, -1, -1, -2, 1, -1, -2, -1, -1, 2, -1, -1, 0, 1, -1, 0, -1, -1, 2, 1, -1, 2, -1, -1, 6, 1, 1, 6, 1, 1, 6, -1
, -1, 6, -1, -1, 4, -1, 1, -6, -1, -1, 6, 1, -1, 6, -1, 1, 6, -1, -1, -6, -1, 1, 4, -1, -1, 4, -1, 1, -4, -1, 1, -6, -1, 1, -2, -1, 1, -4, -1, 1, 0, -1, 1, -2, -1, 1, 2, -1, 1, 0, -1
, 1, 4, 1, 1, 2, -1, 1, -4, 1, 1, -6, 1, 1, -2, 1, 1, -4, 1, 1, 0, 1, 1, -2, 1, 1, 2, 1, 1, 0, 1, -1, 4, 1, 1, 2, 1, -1, -4, -1, -1, -4, 1, -1, -6, 1, -1, -2, -1, -1, -2, 1
, -1, -4, 1, -1, 0, -1, -1, 0, 1, -1, -2, 1, -1, 2, -1, -1, 2, 1, -1, 0, 1, -1, 2, 1]
, "normals": [-1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 1, 0
, 0, 1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0
, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1
, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 1, 1, 0, 0
, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0
, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0
, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0]
, "uvs": [0.625, 0, 0.583, 0.25, 0.583, 0, 0.625, 0.25, 0.583, 0.5, 0.583, 0.25, 0.625, 0.5, 0.583, 0.75, 0.583, 0.5, 0.625, 0.75, 0.583, 1, 0.583, 0.75, 0.375, 0.5, 0.125, 0.75, 0.125, 0.5, 0.875, 0.5, 0.625, 0.75, 0.625, 0.5, 0.417, 0.75, 0.375, 1, 0.375, 0.75, 0.458, 0.75, 0.417, 1, 0.5, 0.75, 0.458, 1
, 0.542, 0.75, 0.5, 1, 0.583, 0.75, 0.542, 1, 0.417, 0.5, 0.375, 0.75, 0.375, 0.5, 0.458, 0.5, 0.417, 0.75, 0.5, 0.5, 0.458, 0.75, 0.542, 0.5, 0.5, 0.75, 0.583, 0.5, 0.542, 0.75, 0.417, 0.25, 0.375, 0.5, 0.375, 0.25, 0.458, 0.25, 0.417, 0.5, 0.5, 0.25, 0.458, 0.5, 0.542, 0.25, 0.5, 0.5, 0.583, 0.25
, 0.542, 0.5, 0.417, 0, 0.375, 0.25, 0.375, 0, 0.458, 0, 0.417, 0.25, 0.417, 0, 0.5, 0, 0.458, 0.25, 0.458, 0, 0.542, 0, 0.5, 0.25, 0.5, 0, 0.542, 0.25, 0.542, 0, 0.625, 0.25, 0.625, 0.5, 0.625, 0.75, 0.625, 1, 0.583, 1, 0.375, 0.75, 0.875, 0.5, 0.875, 0.75, 0.625, 0.75, 0.375, 1
, 0.583, 0.75, 0.583, 1, 0.417, 0.75, 0.375, 0.75, 0.458, 0.75, 0.417, 0.75, 0.5, 0.75, 0.458, 0.75, 0.542, 0.75, 0.5, 0.75, 0.583, 0.5, 0.542, 0.75, 0.417, 0.5, 0.375, 0.5, 0.458, 0.5, 0.417, 0.5, 0.5, 0.5, 0.458, 0.5, 0.542, 0.5, 0.5, 0.5, 0.583, 0.25, 0.542, 0.5, 0.417, 0, 0.417, 0.25, 0.375, 0.25
, 0.458, 0, 0.458, 0.25, 0.417, 0.25, 0.5, 0, 0.5, 0.25, 0.458, 0.25, 0.542, 0, 0.542, 0.25, 0.5, 0.25, 0.542, 0.25]
, "matricesWeights": [0.98, 0, 0, 0, 0.08, 0.91, 0, 0, 0.08, 0.91, 0, 0, 0.98, 0, 0, 0, 0.08, 0.91, 0, 0, 0.08, 0.91, 0, 0, 0.98, 0, 0, 0, 0.08, 0.91, 0, 0, 0.08, 0.91, 0, 0, 0.98, 0, 0, 0, 0.08, 0.91, 0, 0, 0.08, 0.91, 0, 0, 0.98, 0
, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0
, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.91, 0, 0, 0.08, 0.82, 0.09, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82
, 0.08, 0, 0.08, 0.91, 0, 0, 0.08, 0.82, 0.09, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.91, 0, 0
, 0.08, 0.82, 0.09, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.82
, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.09, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.08, 0.91, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0, 0.98, 0, 0, 0
, 0.08, 0.91, 0, 0, 0.08, 0.91, 0, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.91, 0, 0, 0.08, 0.82, 0.09, 0, 0.91, 0.08
, 0, 0, 0.98, 0, 0, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.91, 0, 0, 0.08, 0.82, 0.09, 0, 0.91, 0.08, 0, 0, 0.91, 0.08, 0, 0, 0.98, 0, 0, 0
, 0.09, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.91, 0.08, 0, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.82, 0.08, 0, 0.09, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.09, 0, 0.08, 0.82, 0.08, 0, 0.08, 0.82, 0.09, 0]
, "matricesIndices": [4, 1027, 1027, 4, 1027, 1027, 4, 1027, 1027, 4, 1027, 1027, 0, 0, 0, 4, 4, 4, 256, 0, 0, 131328, 256, 197121, 131328, 262914, 197121, 1027, 262914, 256, 0, 0, 131328, 256, 197121, 131328, 262914, 197121, 1027, 262914, 256, 0, 0, 131328, 256, 197121, 131328, 262914, 197121, 1027
, 262914, 256, 0, 0, 131328, 256, 256, 197121, 131328, 131328, 262914, 197121, 197121, 262914, 262914, 4, 4, 4, 4, 1027, 0, 4, 4, 4, 0, 1027, 1027, 256, 0, 131328, 256, 197121, 131328, 262914, 197121, 1027, 262914, 256, 0, 131328, 256, 197121, 131328, 262914, 197121, 1027, 262914, 256, 256, 0
, 131328, 131328, 256, 197121, 197121, 131328, 262914, 262914, 197121, 262914]
, "indices": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 18, 23, 24, 21, 25, 26, 23, 27, 28, 25, 29, 30, 31, 32, 33, 29, 34, 35, 32, 36, 37, 34, 38, 39, 36, 40, 41, 42
, 43, 44, 40, 45, 46, 43, 47, 48, 45, 49, 50, 47, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 2, 63, 64, 0, 65, 1, 3, 66, 4, 6, 67, 7, 9, 68, 69, 12, 70, 13, 71, 72, 73, 18, 22, 74, 21, 24, 22
, 23, 26, 24, 25, 28, 26, 75, 76, 28, 29, 77, 78, 32, 79, 80, 34, 81, 82, 36, 83, 84, 85, 7, 86, 40, 87, 88, 43, 89, 90, 45, 91, 92, 47, 93, 94, 95, 4, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108
, 2, 1, 109]
, "subMeshes": [{ "materialIndex": 0, "verticesStart": 0, "verticesCount": 110, "indexStart": 0, "indexCount": 156 }]
, "instances": []
}
]
}
window.data = data
BABYLON.SceneLoader.Append('', `data: ${JSON.stringify(data)}`, scene, () => {
const cube = scene.getMeshByName("Cube");
cube.position.x += 4 // move it over so we can see both
const viewer = new BABYLON.Debug.SkeletonViewer(cube.skeleton, cube, scene, false, 3, {
displayMode: BABYLON.Debug.SkeletonViewer.DISPLAY_SPHERE_AND_SPURS,
});
try {
new BABYLON.Debug.SkeletonViewer(mesh.skeleton, mesh, scene, false, 3, {
displayMode: BABYLON.Debug.SkeletonViewer.DISPLAY_SPHERE_AND_SPURS,
});
let shader = BABYLON.Debug.SkeletonViewer.CreateSkeletonMapShader({ skeleton: mesh.skeleton }, scene)
mesh.material = shader
} catch (e) {
console.error(e)
}
viewer.isEnabled = true
})
return scene;
(Sorry, I know how to use the playground but can not copy/paste into it from the ipad - maybe there should be a file uploader)