SceneKitでスキニングアニメーション / magicien 

MMDで言うと、ボーンを動かす普通のアニメーション。これをSceneKitではどう実現するか。


SCNSkinnerを使うべし。

面倒なのは、SCNSkinnerを作る時に、ボーンのローカル姿勢の逆行列を作って渡してあげないといけないこと。
SCNMatrix4Invert(node.transform) で簡単に作れるので、自分で勝手に作ってくれれば良いと思うのだけれど、node.transformの値が初期姿勢とは限らない、というのを考慮してのことでしょう。面倒ですね。
作るとしたらこんな感じ。
var bones = [SCNNode]()

// ここでbonesに適当にボーンを詰める

var boneInverseBindTransforms = [NSValue]()
for bone in bones {
    boneInverseBindTransforms.append(NSValue(scnMatrix4: SCNMatrix4Invert(bone.transform)))
}

let skinner = SCNSkinner(baseGeometry: node.geometry, bones: bones, boneInverseBindTransforms: boneInverseBindTransforms, boneWeights: boneWeights, boneIndices: boneIndices)
node.skinner = skinner
アニメーションさせる頂点データをbaseGeometryに設定する。
bonesはボーンの配列。順番は、boneInverseBindTransformsと一致させる必要があるし、boneIndicesでも使うので忘れないように。
boneInverseBindTransformsは上記要領で用意してやる。
boneWeights、boneIndicesはSCNGeometrySourceのインスタンス。semanticには、.boneWeights、.boneIndicesをそれぞれ設定する。vectorCountは頂点数と一致させる。componentsPerVectorは1頂点に影響するボーン数の最大値。影響するボーン数はいくつでも設定できるが、5以上にするとGPUで計算できなくなり、CPUを使うので遅くなる。MMDの場合は最大4だと思うので大丈夫。で、boneIndicesの各ベクトルに影響するボーンのbones配列でのインデックス番号を設定し、boneWeightsの各ベクトルに影響する割合を設定すれば良い。言葉で書くとわけがわからん。

node.skinner(baseGeometryで指定したgeometryを持っているノードと一致させる。させないとどうなるかはよくわからん)に作ったSCNSkinnerを設定してやれば、ボーンの動きに合わせて頂点が動くようになる。

2017/06/18(Sun) 16:21:19