This is undocumented, so I'm asking here. I'm trying to animate a mesh in JavaScript. I'm using Blender->Three.js exporter because it is convenient. I can't use Three.js itself because I was not able to figure out how to solve certain problems on it (rendering the normals and depth informations of a scene with animated meshes to a buffer). So, how do you read the "skinIndices" and "skinWeights" properties that get exported from Blender to Three.js? What do they mean, and what are they roles when calculating the position of the vertices on the animations?
"bones" : [
{"parent":-1,"name":"pelvis","pos":[-3.52132e-08,0.0410043,0.880063],"rotq":[0,0,0,1]},
{"parent":0,"name":"thigh.L","pos":[0.0878887,0.00522349,0.102822],"rotq":[0,0,0,1]},
{"parent":1,"name":"shin.L","pos":[0.103679,0.00638392,-0.445744],"rotq":[0,0,0,1]},
{"parent":2,"name":"foot.L","pos":[0.0655578,0.0194668,-0.418675],"rotq":[0,0,0,1]},
{"parent":3,"name":"toe.L","pos":[0.0280578,-0.107185,-0.0704246],"rotq":[0,0,0,1]},
{"parent":3,"name":"heel.L","pos":[3.58224e-05,0.036576,-0.0885088],"rotq":[0,0,0,1]},
{"parent":0,"name":"thigh.R","pos":[-0.0878888,0.00522352,0.102822],"rotq":[0,0,0,1]},
{"parent":6,"name":"shin.R","pos":[-0.103679,0.00638412,-0.445745],"rotq":[0,0,0,1]},
{"parent":7,"name":"foot.R","pos":[-0.0655576,0.0194677,-0.418675],"rotq":[0,0,0,1]},
{"parent":8,"name":"toe.R","pos":[-0.0280577,-0.107185,-0.0704248],"rotq":[0,0,0,1]},
{"parent":8,"name":"heel.R","pos":[-3.57926e-05,0.036576,-0.0885083],"rotq":[0,0,0,1]},
{"parent":0,"name":"stomach","pos":[5.37268e-09,-0.008465,0.121596],"rotq":[0,0,0,1]},
{"parent":11,"name":"chest","pos":[1.94616e-08,0.0538289,0.269019],"rotq":[0,0,0,1]},
{"parent":12,"name":"upper_arm.L","pos":[0.160045,-0.010388,0.159844],"rotq":[0,0,0,1]},
{"parent":13,"name":"forearm.L","pos":[0.165089,0.0102809,-0.232678],"rotq":[0,0,0,1]},
{"parent":14,"name":"hand.L","pos":[0.0980782,-0.0148839,-0.245313],"rotq":[0,0,0,1]},
{"parent":15,"name":"index.L.001","pos":[0.019191,-0.040475,-0.0743723],"rotq":[0,0,0,1]},
{"parent":16,"name":"index.L.002","pos":[-0.00562334,-0.00824448,-0.0310695],"rotq":[0,0,0,1]},
{"parent":17,"name":"index.L.003","pos":[-0.00953785,-0.00126594,-0.0192741],"rotq":[0,0,0,1]},
{"parent":15,"name":"middle.L.001","pos":[0.0191911,-0.0188201,-0.0769786],"rotq":[0,0,0,1]},
{"parent":19,"name":"middle.L.002","pos":[0.00288424,-0.00695575,-0.0326532],"rotq":[0,0,0,1]},
{"parent":20,"name":"middle.L.003","pos":[-0.0111618,-0.00550338,-0.0242877],"rotq":[0,0,0,1]},
{"parent":15,"name":"ring.L.001","pos":[0.0186397,0.00194495,-0.0777299],"rotq":[0,0,0,1]},
{"parent":22,"name":"ring.L.002","pos":[0.00393239,-0.00062982,-0.0309386],"rotq":[0,0,0,1]},
{"parent":23,"name":"ring.L.003","pos":[-0.00873661,-0.00165674,-0.024165],"rotq":[0,0,0,1]},
{"parent":15,"name":"pinky.L.001","pos":[0.0191911,0.02271,-0.0758559],"rotq":[0,0,0,1]},
{"parent":25,"name":"pinky.L.002","pos":[-0.0057596,0.0014303,-0.0236881],"rotq":[0,0,0,1]},
{"parent":26,"name":"pinky.L.003","pos":[-0.00877053,-0.0020119,-0.0195478],"rotq":[0,0,0,1]},
{"parent":15,"name":"thumb.L.001","pos":[-0.0073517,-0.0318671,-0.0156776],"rotq":[0,0,0,1]},
{"parent":28,"name":"thumb.L.002","pos":[-0.00941652,-0.0166059,-0.0179188],"rotq":[0,0,0,1]},
{"parent":29,"name":"thumb.L.003","pos":[-0.0081799,-0.0129757,-0.0276645],"rotq":[0,0,0,1]},
{"parent":12,"name":"upper_arm.R","pos":[-0.160044,-0.010388,0.159844],"rotq":[0,0,0,1]},
{"parent":31,"name":"forearm.R","pos":[-0.165089,0.0102809,-0.232679],"rotq":[0,0,0,1]},
{"parent":32,"name":"hand.R","pos":[-0.0980774,-0.0148839,-0.245313],"rotq":[0,0,0,1]},
{"parent":33,"name":"index.R.001","pos":[-0.0185038,-0.0404748,-0.0743726],"rotq":[0,0,0,1]},
{"parent":34,"name":"index.R.002","pos":[0.00562337,-0.00824449,-0.0310695],"rotq":[0,0,0,1]},
{"parent":35,"name":"index.R.003","pos":[0.00953785,-0.00126596,-0.0192741],"rotq":[0,0,0,1]},
{"parent":33,"name":"middle.R.001","pos":[-0.0185038,-0.0188199,-0.0769789],"rotq":[0,0,0,1]},
{"parent":37,"name":"middle.R.002","pos":[-0.00288421,-0.00695577,-0.0326532],"rotq":[0,0,0,1]},
{"parent":38,"name":"middle.R.003","pos":[0.0111619,-0.00550339,-0.0242877],"rotq":[0,0,0,1]},
{"parent":33,"name":"ring.R.001","pos":[-0.0179525,0.00194514,-0.0777302],"rotq":[0,0,0,1]},
{"parent":40,"name":"ring.R.002","pos":[-0.00393245,-0.000629827,-0.0309386],"rotq":[0,0,0,1]},
{"parent":41,"name":"ring.R.003","pos":[0.00873658,-0.00165676,-0.024165],"rotq":[0,0,0,1]},
{"parent":33,"name":"pinky.R.001","pos":[-0.0185039,0.0227101,-0.0758562],"rotq":[0,0,0,1]},
{"parent":43,"name":"pinky.R.002","pos":[0.0057596,0.00143027,-0.0236881],"rotq":[0,0,0,1]},
{"parent":44,"name":"pinky.R.003","pos":[0.00877053,-0.00201192,-0.0195478],"rotq":[0,0,0,1]},
{"parent":33,"name":"thumb.R.001","pos":[0.00803882,-0.0318669,-0.0156779],"rotq":[0,0,0,1]},
{"parent":46,"name":"thumb.R.002","pos":[0.00941664,-0.0166059,-0.0179188],"rotq":[0,0,0,1]},
{"parent":47,"name":"thumb.R.003","pos":[0.00817987,-0.0129757,-0.0276645],"rotq":[0,0,0,1]},
{"parent":12,"name":"neck","pos":[1.6885e-08,-0.0164749,0.225555],"rotq":[0,0,0,1]},
{"parent":49,"name":"head","pos":[0.000806741,-0.0273245,0.0637051],"rotq":[0,0,0,1]}],
"skinIndices" : [
11,0,11,0,1,11,11,0,0,11,0,11,1,11,1,11,0,11,11,0,11,0,11,0,1,11,11,0,11,0,0,11,1,11,1,11,0,11,0,11,0,11,12,0,0,11,0,11,
0,11,12,0,12,0,11,0,11,0,11,0,12,0,11,0,11,0,11,0,12,0,12,11,11,0,11,0,12,13,11,0,0,11,11,0,12,13,12,13,0,11,12,13,12,0,
12,0,13,12,13,12,12,13,12,0,12,13,13,12,12,13,12,13,13,12,13,0,12,13,12,0,12,13,13,0,12,0,13,0,0,13,13,0,13,0,0,13,0,13,
13,0,13,0,0,13,13,12,13,12,13,12,13,0,13,0,13,0,13,12,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,0,13,
(... too big)]
"skinWeights" : [
0.454566,0.443267,0.456435,0.4405,0.568642,0.331477,0.452697,0.446034,0.600277,0.577654,0.603738,0.578153,0.557686,0.334716,
0.579597,0.328238,0.596817,0.577156,0.481496,0.447683,0.604872,0.59171,0.466162,0.448242,0.567426,0.35812,0.49683,0.447124,
0.618979,0.590887,0.592533,0.590764,0.578989,0.341559,0.555862,0.37468,0.477411,0.438341,0.617349,0.569542,0.454728,0.432345,
0.401061,0.337472,0.500093,0.444338,0.633534,0.572105,0.601164,0.56698,0.388198,0.308292,0.413925,0.366652,0.449179,0.424051,
0.618298,0.58735,0.458406,0.430254,0.473939,0,0.439952,0.417849,0.605333,0.579977,0.631263,0.594722,0.517687,0,0.430191,0.274572,
(... too big)]
"animations" : [
{"name":"ArmatureAction",
"fps":24,
"length":0.625,
"hierarchy":
[{"parent":-1,"keys":[
{"time":0,"pos":[-3.52132e-08,0.0410043,0.880063],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[-3.52132e-08,0.0410043,0.880063]},
{"time":0.625,"pos":[-3.52132e-08,0.0410043,0.880063],"rot":[0,0,0,1],"scl":[1,1,1]}]
},
{"parent":0,"keys":[
{"time":0,"pos":[0.0878887,0.00522349,0.102822],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[0.0878887,0.00522349,0.102822],"rot":[-0.36166,-1.53668e-08,-7.05768e-10,0.93231]},
{"time":0.625,"pos":[0.0878887,0.00522349,0.102822],"rot":[0,0,0,1],"scl":[1,1,1]}
]},
{"parent":1,"keys":[
{"time":0,"pos":[0.103679,0.00638392,-0.445744],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[0.103679,0.00638392,-0.445744]},
{"time":0.625,"pos":[0.103679,0.00638392,-0.445744],"rot":[0,0,0,1],"scl":[1,1,1]}
]},
{"parent":2,"keys":[
{"time":0,"pos":[0.0655578,0.0194668,-0.418675],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[0.0655578,0.0194668,-0.418675]},
{"time":0.625,"pos":[0.0655578,0.0194668,-0.418675],"rot":[0,0,0,1],"scl":[1,1,1]}
]},
{"parent":3,"keys":[
{"time":0,"pos":[0.0280578,-0.107185,-0.0704246],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[0.0280578,-0.107185,-0.0704246]},
{"time":0.625,"pos":[0.0280578,-0.107185,-0.0704246],"rot":[0,0,0,1],"scl":[1,1,1]}
]},
{"parent":4,"keys":[
{"time":0,"pos":[3.58149e-05,0.036576,-0.0885088],"rot":[0,0,0,1],"scl":[1,1,1]},
{"time":0.291667,"pos":[3.58149e-05,0.036576,-0.0885088]},
{"time":0.625,"pos":[3.58149e-05,0.036576,-0.0885088],"rot":[0,0,0,1],"scl":[1,1,1]}
]},
Each vertex corresponds to one skin index which corresponds to one skin weight. The skin index is the index of the bone that the particular vertex is influenced by (each vertex can only belong to one bone). The skin weight is the amount of influence that bone has over that vertex.
The skinIndices
and skinWeights
properties are arrays of arrays (technically the inner arrays are three.js Vector4
objects). Each item in the outer array of either corresponds, one-to-one, based on the indexed position, with each vertex in the mesh.
Here's the relevant JSONLoader
code that creates the values for these properties from a three.js JSON model file:
if ( json.skinWeights ) {
for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {
var x = json.skinWeights[ i ];
var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;
geometry.skinWeights.push( new Vector4( x, y, z, w ) );
}
}
if ( json.skinIndices ) {
for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {
var a = json.skinIndices[ i ];
var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;
geometry.skinIndices.push( new Vector4( a, b, c, d ) );
}
}
The docs for the three.js Geometry
object were updated to include info about this [some formatting and two inconsistency corrections mine]:
Just like the
skinWeights
property, theskinIndices
' values correspond to the geometry's vertices. Each vertex can have up to 4 bones associated with it. So if you look at the first vertex, and the first skin index, this will tell you the bones associated with that vertex. For example the first vertex could have a value of( 10.05, 30.10, 12.12 )
. Then the first skin index could have the value of( 10, 2, 0, 0 )
. The first skin weight could have the value of( 0.8, 0.2, 0, 0 )
. In affect this would take the first vertex, and then the bonemesh.bones[10]
and apply it 80% of the way. Then it would take the bonemesh.bones[2]
and apply it 20% of the way. The next two values have a weight of0
, so they would have no affect.In code another example could look like this:
// e.g. geometry.skinIndices[15] = new THREE.Vector4( 0, 5, 9, 0 ); geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3, 0 ); // corresponds with the following vertex geometry.vertices[15]; // these bones will be used like so: skeleton.bones[0]; // weight of 0.2 skeleton.bones[5]; // weight of 0.5 skeleton.bones[9]; // weight of 0.3 skeleton.bones[10]; // weight of 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With