spine morph-target

本家より良い返事が貰えたので、経緯を残しておきます。そのうち使えるようになるそうです。ありがたし!


ver3.6のspineを弄っているとメッシュデフォームがあるものの、口パクや表情でよく使うモーフターゲット(morph-target)がないので、固定的なものは力業でなんとかなるとしても、動的な表情変化などの実現が厳しいなぁと気づくと思います。
なんとかしたいですよねナナチ。
幸いランタイムのソースコードがフルオープンなので中を覗けますし、(ライセンスの範囲内で)勝手に弄ってもOKなので、なんとかできないかなーと休日眺めてみました。

サンプル


どういったものかはサンプルで。

  • spine-c morph-target customize sample

https://1drv.ms/u/s!AvtcMsC8irYBgzJkqmizB2me52T1

まず過去ログ

フォーラムの過去ログ(英語)を眺めてみたら、過去に2度ほど話題には上がったみたいです。
少なからず同じような悩みはあったみたいですね。

まずモーフターゲットが実装できるか

モーフターゲットを実現させるにはいくつかの条件が必要です。

  • 変形前のベースとなる形状が必要

名前の通りベースとなる”ターゲット”が必要になります。これは変形前の形状で、変形後との差分の計算に使用されます。
差分はそのままベースに加算されていき、1つのオブジェクトに変形が複数ミックスされていきます。

diff? = Animation? - base
out = base + diffA + diffB + diffC + diffD ...

みたいな感じです。
幸いspineにはベースが存在します。エディタではSETUPとして部品を組み立てるモードがありますね。あれです。あれを使いましょう。
プログラム上だとちょっとわかりにくいですがskeletonから追跡できます。Animation.cを見れば手っ取り早いかなと思います。

  • 変形後の形状もベースと同じ構造をしていること

これはSETUPから変形させて作られたアニメーションのことを指します。
モーフターゲットの場合はベースと構造が同じでなくてはいけません。メッシュなら頂点数とかポリゴンの張り方とか。
幸いspineも同じガイドラインを敷いているので問題無さそうです。

  • それら複数の形状を指定できる環境

複数合成するのでその土壌がなければいけません。幸いアニメーションはトラックという概念で複数扱えるので問題ありません。

  • ということで

エディタ上では難しいけど、ランタイムのアニメーショントラックに仕込めば実現できそうです。
エディタだとプレビューのあそこですね。タイムラインではありません。

実装

spine-c ver3.6を使用します。実装の大半はAnimation.cになります。
「void _spDeformTimeline_apply」あたりを見てみると分かりやすいと思います。
前半と中盤は定義キーが無い前と後ろの処理、要するに例外処理なので飛ばします。
一番最後あたりにアニメーションの合成にあたるコードがあります。

for (i = 0; i < vertexCount; i++) {
      float prev = prevVertices[i];
      float v = prev + (nextVertices[i] - prev) * percent;
      vertices[i] += (v - vertices[i]) * alpha;
}

prevVerticesが現在位置より後方の変形情報、nextVerticesが現在位置より前方の変形情報です。
percentがキー間のブレンド率なのでvが現在地点での変形座標。alphaがモーションのブレンド率なのでアニメーション間の合成だけ抜き出せば

out = out + (v - out) * alpha

と単純な構造です。ここをモーフターゲットに改良するだけです。


モーフターゲットはベースとなる変形前の座標が必要になります。
spineでいえばSETUPですが、このコードの上あたりを見ると―

case SP_MIX_POSE_SETUP:
    if (!vertexAttachment->bones) {
        memcpy(vertices, vertexAttachment->vertices, vertexCount * sizeof(float));
    } else {
        for (i = 0; i < vertexCount; i++) vertices[i] = 0;
    }
...

まんまがあります。
「vertexAttachment->vertices」がSETUP、変形前のベース情報になります。
SP_MIX_POSE_SETUPは最初に投げられるもので、変形前の状態を変形バッファにコピーしてる感じです。


これで必要な要素は揃いました。
あとは以下の公式に変形すればいいだけです。

out = out + (A - SETUP) * alpha

なので

float* setupVertices = vertexAttachment->vertices;
for (i = 0; i < vertexCount; i++) {
    float prev = prevVertices[i];
    float v = prev + (nextVertices[i] - prev) * percent;
    vertices[i] += (v - setupVertices[i]) * alpha;
}

となります。おわり。

本当はもうちょっと複雑

アルゴリズムの話は以上でおしまいですが、実際はもうちょっと複雑です。計算が複雑というわけではなく、例外処理、最適化処理との相性、言語的な部分などなど。
今までアニメーションは上書きだったので、alpha1.0だとそれより前のアニメーション処理要らないですね!なども組み込まれているので対処が必要です。
実際に組み込むには20カ所ほど変更や修正が必要です。
実装されるまで待ちましょう :Q