2012年9月30日日曜日

[LSL] ネギを投げるスクリプト

ネギを投げるスクリプト、、、ネギじゃなくても、モーションと組み合わせて「投げる」ということで結構苦労されている方も多いようなので、ひさしぶりに LSL の解説です。

で、みなさん苦労されているのは、いつもでてくる「グローバル座標軸」と「ローカル座標軸」、そして、アタッチしたときの軸の考え方、最後にダンス(アニメーション)中のアバタ―の向きの考え方が難しいのでは?と思います。

グローバルとローカル座標軸

グローバル座標軸とローカル座標軸の基本は以下の図になります。

グローバルローカル軸

これは、、OK ですね?

グローバル座標軸(東西)上で 1m 進むときのベクトル型の表現は <1.0, 0.0, 0.0> です。では、上の図のローカル座標軸(前後)で 1m 進むときのベクトル座標の表現はどうなるでしょう?
<1.0, 0.0, 0.0> としたいところですが、これではだめで、日本語で表現すると「今見ている方向の前に 1m 進むためには、東に X m、南に Y m、天に Z m 進む」としなければなりません。

えー、じゃー、ピタゴラスの定理を使って計算?ではありません。このような時の便利な関数が llRot2Fwd です。今向いている方向の前に1m進むためのグローバル座標軸上での移動距離を返してくれます。llRot2Fwd(今向いている方向) で 1m 前に進むためのベクトル型の数値を得ることができます。

では、今、向いている方向はどう取得するか、ということですが、それは llGetRot() になります。

なので、今向いている方向の前方に 1m 進むためのグローバル座標軸上の移動距離は

vector xv = llRot2Fwd(llGetRot());

でとれます。同様に今向いている方向の左右を yv、上下を zv とすると以下のようになります。

vector yv = llRot2Left(llGetRot());
vector zv = llRot2Up(llGetRot());

え? 50cm の場合は?うん、その場合は xv*0.5 でいいんですよ。3m の場合は xv*3 です。
前に1m、左に1m、上に1m の地点(targetV)までいくには、xv+yv+zv を移動すればいいわけです。

vector targetV = llRot2Fwd(llGetRot())+llRot2Left(llGetRot())+llRot2Up(llGetRot());

この長い targetV の式がポイントになります。

装着したオブジェクトとローテーション

ネギを投げるにはネギを「持って」いなければいけません。持つということは装着する、ということですが、スクリプトはその装着されたネギ(オブジェクト)に入ることになります。

すると、装着したオブジェクトの方向とアバタ―の方向は違ってきます。

装着軸

実線が装着したオブジェクトの前後左右上下軸で、破線がアバタ―の前後左右上下軸です。

装着したオブジェクト内のスクリプトでは両方のローテーションを取ることができます。

アバタ―の向き llGetRot()
オブジェクトの向き llGetLocalRot()

(正確に言うと、装着するオブジェクトが複数のプリムのリンクによって作成された場合は、ルートプリムか、子プリムかで変わりますが、まずは1プリム(もしくはスクリプトはルートプリムにしか入れない)をしっかりと理解しましょう)

イメージとしては、拳銃みたいなオブジェクトで、つねに銃身の向いている方向から弾丸を出したい、というような場合はオブジェクト(拳銃)の向きが重要ですよね。(あくまでイメージです。次のアニメーションの考え方を合わせないと投げるモーションや銃身から玉がでるスクリプトはうまくできませんよ)

アバタ―が向いている方向は、装着したオブジェクトの中から llGetRot() で取れる、と理解してくださいね。

ダンスをしている時のアバタ―/装着オブジェクトの向き

これが、、、落とし穴です。
ダンスをしていると、アバタ―が動きまわり、手にもったオブジェクトも動いているように見えます。でも、実はスクリプトから取れるアバタ―/オブジェクトの向きや位置はまったく変化していないのです。

うそーん?と思う方は以下の動画をご覧ください。3秒おきに llGetPos、llGetRot、llGetLocalPos、llGetLocalRot の数値をスクリプトが llOwnerSay で通知します。LocalPos や LocalRot は右手にもっているオブジェクトの既定装着場所からの相対的な位置・向きです。オブジェクトを持つ位置や向きはまったく変えていないのでゼロが並びます。llGetPos, llGetRot はアバタ―の位置と向きになりますよね。アバタ―が動けば変わるはず、、、、です。

アバターがダンスモーションで動き回っているのに、llGetPos() で取れる数値は踊っている時は <200.33750, 234.75000, 2495,57000> のままで、llGetRot() で取れる向きはゼロ(これは、常にゼロ、という意味ではなく、グローバル座標軸上で東を向いている状態です。向きが変わればゼロではありません)になっています。もちろん、ダンスを始める前、ダンスをやめた後で歩き回っている時の位置や向き(Z軸のみ)は変わっています。

ダンスを踊っている(モーションを再生している)時に動いているアバタ―って、ファントムのようにすり抜けるでしょ?実体はあくまでダンスを開始する直前の位置や向きのまま、モーションによってアバタ―が動いている「ように」見えている、とイメージしてください。なので、よく「ステージ上でダンスしているアバタ―をライトで追いかけるスクリプト」なんて話がでるのですが、ダンスしているアバタ―の位置は装着スクリプトなどでは取れないので簡単ではないのです。

相対位置からネギを投げる(REZする)

ではどうするか?ということですが、オブジェクトを投げる瞬間のアバタ―の位置もしくはオブジェクトが出現(REZ)される位置を、実体がいる場所からどのくらい離れているかを見つけるしかありません。実体との相対位置、相対角度は、実体がどんな位置にいようが、方向を向いていようが変りません。相対位置と角度がわかれば、「そのタイミング」でオブジェクトをREZ すればいい、ということですね。

相対位置と向きは、、、試行錯誤で見つけるしかありません。とはいえ、以下のようなスクリプトで相対位置と角度を探すことができます。

使い方:
・チャンネル 39 を使ったチャットコマンドです。もちろんソースから変更可能です。
・投げるオブジェクトを、スクリプトの入ったプリムのコンテンツフォルダーにいれます。(オブジェクトの属性は、物理属性でかつ一時にするとよいでしょう、、というか、してください)
プリムを装着して、/39 shoot でオブジェクトがでます。(物理設定されていれば、ポロン、、、と落ちますw。されていないとその場にとどまります。)
・上に飛ばしたい場合は /39 velz 10 といれると、ローカルのz軸(上下)の上方向に初速 10 m/s で rez されるように設定されます。/39 shoot で確認できます。
・前に飛ばしたい場合は /39 velx 10 といれます。前方かつ上方向の場合は /39 velz 10 のあとで、/39 velx 10 とすることで前方向と上方向を合成した「斜め前上」に向かって rez されます。
・位置は /39 posx 2 で前方向2m進んだ場所から rez される設定になります。/39 posx –2 で後ろに下がります。/39 posx -2 のあとで /39 posx –4 とすると、6m 下がった位置(-6)になります。posy や posz で左右、上下の調整が可能です。
・REZされるオブジェクトの向き(発射方向ではありません。REZされた瞬間のオブジェクトの向きです)は /39 rotz 10 などで z 軸で10度、という調整可能です。
・/39 info で設定している値を表示します。
・そのほかのコマンドもありますが、、、ソース見て下さいw
・最終的に相対位置と向きがきまったら、そのベクトル型の数値を 4行目の vector rezVel (投げ出す方向と速度)と 7行目の vector OFFSET に設定してあげると、/39 shoot だけで投げるようになります。
・ただし、以下のサンプルはエラー、例外処理がまったくありません。あくまで相対位置を探し、オブジェクトを発射するサンプルとして考えてくださいね。 

integer cmdChan = 39;
integer cmdHandle;
string rezObject = "";
vector rezVel;
vector rezRotV;
integer rezParam;
vector OFFSET;
default
{
    state_entry()
    {
        string s = llGetInventoryName(INVENTORY_OBJECT,0);
        if(s!=""){
            rezObject=s;
        }
        //オーナーの cmdChan のSayのみ
        cmdHandle = llListen(cmdChan,"",llGetOwner(),"");
        llListenControl(cmdHandle,TRUE);
    }
   
    attach(key id)
    {
        if(id!=NULL_KEY){
            llResetScript();
        }
    }

    listen(integer chan, string name, key id, string msg)
    {
        list t = llParseString2List(msg,[" "],[""]);
        string sCmd = llToUpper(llList2String(t,0));
        llOwnerSay("cmd:"+sCmd);
        if(sCmd=="SHOOT"){
            llOwnerSay(rezObject);
            if(rezObject!=""){
                llRezObject(rezObject,
                            //Position 投げ出す位置
                            llGetPos()+
                            llRot2Fwd(llGetRot())*OFFSET.x+
                            llRot2Left(llGetRot())*OFFSET.y+
                            llRot2Up(llGetRot())*OFFSET.z,
                            //Velocity 投げ出す方向と速度
                           //投げ出す方向とスピードはここの設定がポイント!!!

                            llRot2Fwd(llGetRot())*rezVel.x+
                            llRot2Left(llGetRot())*rezVel.y+
                            llRot2Up(llGetRot())*rezVel.z,
                            //Rotation 投げ出されるオブジェクトそのものの向き
                            llEuler2Rot(rezRotV)*llGetLocalRot(),
                            rezParam);
            }
            //実際に「投げる」場合はここに自分を消す関数をいれます。
            //複数プリムからなっている場合は llSetLinkPrimitiveParamsFast ですが、
            //プリムの「色」を複数使っている場合は message を使ってプリム間通信で
            //LINK されたプリムの表示・非表示をやります。単体なら llSetAlpha で。

//ここから下は微調整のためのスクリプト
        }else if(sCmd=="POSX"){
            OFFSET.x += llList2Float(t,1);
            llOwnerSay("NEW OFFSET:"+(string)OFFSET);
        }else if(sCmd=="POSY"){
            OFFSET.x += llList2Float(t,1);
            llOwnerSay("NEW OFFSET:"+(string)OFFSET);           
        }else if(sCmd=="POSZ"){
            OFFSET.x += llList2Float(t,1);
            llOwnerSay("NEW OFFSET:"+(string)OFFSET);           
        }else if(sCmd=="POS"){
            OFFSET = llList2Vector(t,1);
            llOwnerSay("NEW OFFSET:"+(string)OFFSET);           
        }else if(sCmd=="VELX"){
            rezVel.x += llList2Float(t,1);
            llOwnerSay("NEW VELOCITY:"+(string)rezVel);
        }else if(sCmd=="VELY"){
            rezVel.y += llList2Float(t,1);
            llOwnerSay("NEW VELOCITY:"+(string)rezVel);           
        }else if(sCmd=="VELZ"){
            rezVel.z += llList2Float(t,1);
            llOwnerSay("NEW VELOCITY:"+(string)rezVel);
        }else if(sCmd=="VEL"){
            rezVel = llList2Vector(t,1);
            llOwnerSay("NEW VELOCITY:"+(string)rezVel);
        }else if(sCmd=="ROTX"){
            rezRotV.x += llList2Float(t,1)*DEG_TO_RAD;
            llOwnerSay("NEW ROT-VECTOR:"+(string)rezRotV);
        }else if(sCmd=="ROTY"){
            rezRotV.y += llList2Float(t,1)*DEG_TO_RAD;
            llOwnerSay("NEW ROT-VECTOR:"+(string)rezRotV);           
        }else if(sCmd=="ROTZ"){
            rezRotV.z += llList2Float(t,1)*DEG_TO_RAD;
            llOwnerSay("NEW ROT-VECTOR:"+(string)rezRotV);           
        }else if(sCmd=="ROT"){
            rezRotV = llList2Vector(t,1)*DEG_TO_RAD;
            llOwnerSay("NEW ROT-VECTOR:"+(string)rezRotV);
        }else if(sCmd=="INFO"){
            llOwnerSay("Global Rot:"+(string)llRot2Euler(llGetRot())+", Local Rot:"+(string)llRot2Euler(llGetLocalRot()));
        }else if(sCmd=="TIMER"){
            if(llToUpper(llList2String(t,1))=="ON"){
                llSetTimerEvent(3.0);
                llOwnerSay("Timer On");
            }else{
                llSetTimerEvent(0.0);
                llOwnerSay("Timer Off");
            }
        }
    }
   
    timer()
    {
        llOwnerSay("llGetPos():"+(string)llGetPos()+",llGetRot():"+(string)(llRot2Euler(llGetRot())*RAD_TO_DEG));
        llOwnerSay("llGetLocalPos():"+(string)llGetLocalPos()+",llGetLocalRot():"+(string)(llRot2Euler(llGetLocalRot())*RAD_TO_DEG));
    }
}

位置、方向を決める長い llRot2Fwd, llRot2Left, llRot2Up と llGetRot の式は最終的には以下にまとめることができます。

//Position 投げ出す位置 の部分
llGetPos()+OFFSET*llGetRot();

//Velocity 投げ出す方向と速度 の部分
rezVel*llGetRot();

最後の仕上げは REZ するタイミング

こちらは前回のブログの記事をご覧いただければと思います。

mldu5.5+ のマクロでチャットコマンド

長くなりましたが、ご参考になれば幸いです~

[追記] 位置や回転(向き)をもっと理解したい~という方は、よろしければ過去に投稿した記事を参考にしてみてください。
[LSL] 位置と回転について (7回にわけて位置と回転について書いています)

2012年9月19日水曜日

上半身2ボーンと chest 対応変換などモーション変換について

以前の投稿で、MikuMikuDance のモーションで「上半身2」ボーンを使ったモーションの変換の話をご紹介しました。

http://snumaw.blogspot.jp/2012/03/secolife-avatar-pmd.html

当時よりちょっとだけ知識が増えたみたいなので、一度まとめてみようと思いました。

1) MMD のモーションを利用する場合は、、、まずは readme を読んでね

改造、利用、再配布条件などモーションを利用させていただくにあたり、大切なことが書かれているのは当然ですが、場合によっては特殊ボーン(標準モデルのボーンとは違うもの、、、と理解してください)についての記載があります。(無いこともありますw)

2) 特殊ボーンが使われていないか確認する

readme にはだいたい「XXXボーン必須のモーションです」という記述があるのですが、特に気を付けたほうがいい特殊ボーンは以下のようなものです。

・上半身2ボーン(これは以降ご紹介するツールで対応可能です)
・腕捩りボーン(左右腕、左右手)(ツールあり、、もしくはさらに手修正で対応可)
・すべての親ボーンやグルーブボーン (すべての親は影響ない場合もありますが、グルーブボーン必須の場合は、ちょっと雰囲気が違うモノになります。体全体でリズムをとるグルーブボーンの動きが変換されないからです。)
・腕IKボーン(変換はあきらめたほうがいいかも。mio で IK 計算できるかは試してません。)

readme に特に記述がなくても、一度、MikuMikuDance で vmd ファイルを読込んで確認したほうがいいです。その際には特殊ボーンがあらかじめ入っているモデルを使ってください。(腕IKは、、、さすがにかなり特殊なので readme にありますし、腕IKモデルは一般的ではありません。が、「うわぁ、すごいダンス!」というモーションで腕IK必須、というのがあるのも現実です)(追記:mio で「全ての親」ボーンがある PMD を使用すると bvh への変換がうまくいかないことが確認されています。全ての親ボーンが初期位置設定くらいしか使われていなければ、全ての親ボーンを無視していい場合がほとんどです。全ての親ボーンを使っていない PMD モデルを mio にて利用してみてくださいね。)

今回は特に「上半身2」ボーンと、腕の「捩りボーン」対策をご紹介します。

3) 上半身2ボーンは、セカンドライフのアバタ―の「chest」に相当します。その時は、、、

といっても、セカンドライフで QAvimator などを使って、ポーズを作ったことがないとさっぱり、、、かもしれません。

これまで一般に紹介してきた mio / MMDBVHToSLBVH では上半身2ボーンの対応モーション変換はできませんでした。MMDBVHToSLBVH が chest への回転データを作成しないことと、mio で「上半身2」ボーン入りのモデルを使わなくてはいけないこと、という2つの要素が重なります。

上半身2?chest ? そんなに違うの?と思われる方は以下の比較動画をご覧ください。

最近の MMD の秀逸なモーションは上半身2ボーン必須モーションが増えてきているので、これまでの MMDBVHToSLBVH では上半身が「暴れ」ます。MMDモデルの下半身ボーンと上半身ボーンは、MMDBVHToSLBVH では Secondlife モデルの「hip(腰)」と「abdomen(腹部)」に対応されます。(*1) これに加えて腰や腹部の動きと逆の動きを上半身2が対応する「chest(胸部)」で行うことで、胸より上の動きを抑えている、というモーションが結構あるからです。

(*1) 正確にいうと、MMDBVHToSLBVH で SL モデルの hip の動きは、MMD モデルの下半身ボーンとセンターボーンを合成させています。正確ではありませんでした。mio で変換された bvh の root と hip を合成しています。mio は MMD モデルのセンターを root に変換しているようです。

いまでも、、、ちょっとどころか、かなり自信が無いのですが、上半身2ボーン必須モーションも増えてきたので、私(とお友達)で esetomo 様が作られた MMDBVHToSLBVH を上半身2ボーン/Chest対応させたものを公開しました。(これをフォークして公開した、というようです。)

snumaw/MMDBVHToSLBVH
https://github.com/snumaw/MMDBVHToSLBVH

Readme は是非とも読んでいただきたいのですが、ツール(MMDBVHToSLBVH.exe)は、[MMDBVHToSLBVH.exe] のリンクをクリックすると、画面の上のほうに [View Raw] というリンクが出るので、それをクリックすればローカルへのダウンロードが始まります。

なお、このツールだけでは上半身2ボーン必須モーションは変換できません。何度も書きますが、mio で bvh に変換する時に使用するモデルに上半身2ボーンが入っていないとだめです。この点は注意してください。(モデル名の英語化も忘れずに!)

上半身2ボーン?確認の仕方がわからない、という人は MMD でモデルを読み込んで、左側のボーンリストを見てください。

同梱のあにまさ式ミクさんV2の場合
mmdbonecheck

人気のままま式GUMIさんの場合、上半身2ボーンがあります。
mmdbonecheck2

4) 腕・手の捩り(ねじり)ボーンを多用しているモーションは2つの対応方法

これは私も結構ハマるパターンです。mio/MMDBVHtoSLBVH で変換し Secondlife にあげてみると「コレジャナイ」感満載のモーションになります。

実はこの「星間飛行」のモーションは両腕捩ボーン、両手捩ボーンを多用したモーションでした。

動画は修正後のものですが、最初、secondlife にアップしてみて、、、、あれ?違う?と思い、MMD を立ち上げてオリジナルモーションを確認すると、、、上半身2はもちろん、捩ボーンを多用していました。

MMD001S

両腕・両手捩ボーンは標準モデルでもついているので使われてるケースは多いのですが、Secondlife に上げてしまうと手首から指にかけては正直相当アバウトなモーションになるので、極端に「肘が捩れている」場合や「腕の付け根が捩れている」場合でなければ気が付かない、もしくは無視しています。

ですが、今回は、どうみても別のモーションになっていました(涙
どのくらい別ものかは、後で、修正前、修正後のビデオでご紹介しますね。

このような場合は2つの方法で対応します。

a) 手作業で修正

星間飛行のモーション、よく見ると左手は常にマイクをもってあまり動きがなく、右手だけの修正で済みそう、、、と思い、実際動画のモーションは手作業で修正しました。

まず、捩りボーンのキーフレームをすべて無効化します。無効化の仕方はフレーム操作ボックスの下にあるフレーム数指定で 0~最後のフレーム で捩りボーンをドロップダウンから選択して、[範囲選択] ボタンを押します。それを繰り返して、すべての腕・手捩ボーンのキーフレームが選択された状態にします。

MMD002S

ここで、[フレーム編集] – [フレーム位置角度補正] という便利な機能を使って、すべての回転を無効にします。メニューバーから選ぶと以下のダイアログボックスが出るので、回転すべてに ゼロ を入れて [OK] を押せばいいんです。

MMD004

この状態が、secondlife にアップしてみた「コレジャナイ」モーションです。なので、もう1体モデルを読込み、同じモーションを読み込ませて、並べて見ながら、腕と肘の回転でだいたいあってる感じの回転を付与します。終わったら、モーションを保存して変換をかけることになります。

b) ツールで一気に修正

これは実は手作業した後で気が付いたのですが、MMD の姉妹ツール MikuMikuMoving 製作者の Mogg さんが便利なツールを1年以上も前に公開されていたんです。。。

VMD Motion Integrator
https://sites.google.com/site/moggproject/

以下、説明
--------------
VMD Motion Integrator
多段ボーン、捩りボーンのモーションを、元の1つのボーンモーションに統合するツールです。
これにより、多段/捩りボーンによるカスタムモデルのモーションを、他の通常モデルに適用することが可能になります。

下記のような感じで統合します。

- キーフレーム位置では補間曲線を使っていてもほぼ完全一致します
- キーフレーム間の補間では一致しないことがあります
- 捩りボーンは特にボーン構造が異なるため、多くの場合、動きが異なります。

* オリジナルファイルを上書きしてしまわないよう注意してください。
--------------

ね?簡単でしょ?。。。。。

とはいえ、そこはツールで一気に変換するわけで、モーション製作者があえて「捩りボーン」を利用している理由はあるわけです。最終的には目視による確認と手付による修正は必要、となります。でも、、、楽ですよね!

では、オリジナル(画面左)、捩りボーン無効(真ん中)、ツール変換で腕に統合(画面右) のモーションをお楽しみください、、、、

画面右の Mogg さんのツールモーションすごいですね。
一番いいのは Mogg さんツールで統合して、以下のような部分を手で修正かな~。

gumishoulder

ご参考になれば、、、(なるのかな?)幸いです~。

2012年9月16日日曜日

mldu5.5+ のマクロでチャットコマンド

こちらの動画で、mldu5.5 と MMD モーションで踊っていますが、今日の本題は「ねぎ」です(笑

この Ievan Polkka のために作りなれないネタ系スクリプトがんばったー><

ねぎの仕様は以下のようなものです。

・ 表示/非表示を左右の手もち状態で。
・ 投げることはできないので、llRezObject で速度をつけてねぎからねぎをRez
・ これらはチャットコマンドで操作可能

Ievan Polkka のモーションファイルは30秒ですが、mldu のマクロは以下のようなものです。

<Polkka MODE:SNUPOZ>
Polkka_1, 30.0, /3939 init
Polkka_2, 1.2
Polkka_2, 28.8, /3939 show left
Polkka_3, 3.8,  /3939 change
Polkka_3, 13.0, /3939 throw
</Polkka>

モーション開始前にねぎを両手にもっておきます。
最初のモーション Polkka_1 をはじめると同時に /3939 init で、右手のねぎ1本表示、左手のねぎは非表示にします。モーション時間は30秒です。
次のモーション Polkka_2 を 1.2 秒再生して、/3939 show left コマンドを発行、左手のねぎを表示します。そのときは残りのモーション 28.8 秒の再生指定になります。
最後のモーション Polkka_3 開始と同時に /3939 change コマンドで、左手のねぎを非表示にして、右手のねぎを2本表示にします。これで左手から右手にねぎをまとめたように見えます。3.8秒再生として、次のコマンド /3939 throw を発行、これで、右手のねぎを非表示にして、llRezObject でねぎ2本を上方向に速度をつけて rez します。のこり 13.0 秒の再生指定もします。

このマクロは MODE:SNUPOZ オプションで可能となる書き方です。

MODE:SNUPOZ は、フォーメーションシステムの SnuPoz 専用じゃなくて、上のようにチャットコマンドとの連動を可能にするもの、と考えると、もっと使い方の幅が広がると思いますよ~。

また、同じモーション指定を続けると、そのままモーションは動いたままで、通常モードだと Sit ボールに座ったアバターの向き・位置指定が可能になり、MODE: SNUPOZ だとチャットコマンドを発生させることができます。

このあたりの考え方は以前もご紹介しています。

タイトルが英語なので、、、、でも、同じモーションをつづけながらのコマンド指定の方法を説明しています。

http://snumaw.blogspot.jp/2012/07/mldu5-notecard-macro-tips-combination.html

ご参考になれば幸いです~。

2012年9月10日月曜日

パンダヒーロー モーション

パンダヒーロー セカンドライフで mmd モーション

マーケットプレースで 0L$ 購入可能になりました!
https://marketplace.secondlife.com/p/MMD-Panda-Hero-motion-files/3954144

根性P様によるモーショントレス、暴徒さん・けいたんさん踊りの「パンダヒーロー」です。
根性P様の読み物.txt( readme ですね )で、モーションの2次配布について、改変データ配布は OK ということ、その際の報告等も特に必要ありません、という条件なので、marketplace での 0 L$ 配布準備をしています。

根性P様のトレスによるパンダヒーロー

mio による vmd –> bvh および bvh –> slbvh の変換の過程で、MMD で動いているようにならない場合がいくつか発生します。また、モデルの体格差・構造の違いにより、どうしても secondlife の中でみると「ん~」となる部分もあります。

肩回りは常々問題となる構造の違いであり、secondlife での利用を考えると、極力、肩は動かさないように修正するのが無難です。また、MMDの「センター」と「左右足IK」との間隔が短くなると、どうしても足IKが「浮いてしまう」こともあります。(しゃがんだ時など)この場合は、センターを上に上げるか、試行錯誤で足を下げるなどの修正を行います。

今回はそれらの修正もしたうえで、「逆立ち」したときの手の位置がどうしても床に埋まるのを回避してみました。上の動画では 3分13秒あたりで RIN が逆立ちするところで、両手が床にかなり埋まっているのが確認できると思います。 (動画で使ったモーションは修正前です)
これを回避する修正をMMDでするとこんな感じです。

panda-gumi

センターと左右両足IKすべて選択して、Y軸(緑)方向の上に該当するキーフレームを上げてやります。試行錯誤でちょうどいい高さを選ぶしかないのですが、結構あがりますよね。

センターと両足 IKの位置と向きが決まると、途中のひざやふとももの根元の回転が決まります。MMD ではこれをリアルタイムに計算しています。めんどくさそうに思えますが、この 両足 IK のおかげで、MMD では床に設置するという動きを容易に作成できるように工夫されています。

MMD モーションを BVH にする際には、この IK から各関節(ひざ、大腿部付け根)の回転を計算しなくてはいけません。それをやっているのが mio です。mio の大きな役割は VMD という MMD モーションファイルを BVH に変換するときに IK 計算を行い、各関節の回転(向き)を算出しているのです。

そのとき、mio は secondlife でいう腰の位置、mmd でいうセンターの位置の変換も当然やっています。センター/IK の計算、モデルの構造(体格)の違いで、どうしてもこういう差がでちゃうんでしょうね。(私はさすがに mio のコード修正とか無理ですw)

mio では大腿部の長さや、すねの部分の長さから適切な関節部分の回転を割り出すので、どうしても参考とする「モデル」が必要となります。mio でモデルを読みこまなければいけない理由は IK の計算のため、ともいえます。読み込むモデルの体形が変われば、変換後の BVH の数値も変わるんですよ。

上の逆立ち修正をして VMD –> SLBVH した時の SS がこちら。

panda-miku

marketplace で無料(0L$)公開しました~
https://marketplace.secondlife.com/p/MMD-Panda-Hero-motion-files/3954144