HSPでOggVorbisな話
HSPさんこんにちは
Write: 03/06/26 UpDate: 03/06/27

どうもOggVorbis開発陣営の動きが鈍くなっている感じがします。
mp3やらwmaに対するアドバンテージが一般ユーザには見えてこないので、
盛り上がりに欠けるのは仕方の無い所でしょうか。
とはいえライセンスフリーというのは開発者としては美味しいので、頑張ってほしいなぁと
西の方角(なんとなく)に向かって念を送る毎日です。
今回は微力ながらOggVorbis普及に努めましょうというネタ。
以前書いて放置しっぱなしのVoxドライバをHSPに対応させてみました。
要はC言語仕様のラッパー関数を被せたのと、引数をintオンリーにしただけなんですけどね。
もちろん以前のようにC++からの呼び出しも可能です。
ついでにライブラリのバージョンを以前のβ版から1.0に上げておいたので、
以前のバージョンを使われている方にもご利益があるかもしれません。
ネットで調べるとDelphiから呼び出そうとして挫折している例がありましたが、
そういった場合にも今回のC言語用インタフェースを使えば何とかなるかもしれません。
とりあえずはHSPに話を絞ります。
ゲーム製作に向いたスクリプト言語として有名なHSPですが、私は使ったことがないので正直どんな言語か良く知りません。
そんな訳で、掲示板にVoxはHSPで使えないのかという話が舞い込んだ時に慌ててインストールして
30分ほどいじったというのが全使用時間。
結果、C関数なら外部DLLから呼び出せるということが分かりました。
HSPの仕様として浮動小数点が使えないというものがあるらしく、かなりショックを受けましたが、
とりあえず引数をintにしたラッパー関数を用意すれば解決する話です。
以上を踏まえた新バージョンVoxです。ソースはこちら
HSPから使用するサンプルスクリプトはこんな感じです。

#include "llmod.as"
ll_libload hDLL,"Vox.dll"


;.ogg ファイルのロード
msg = "test.ogg" : getptr pm, msg
dllproc "VoxLoad", pm, 1, hDLL
VoxID = dllret


;ループ回数設定
pm = VoxID, 10 : dllproc "VoxSetLoop", pm, 2, hDLL

;再生
pm = VoxID : dllproc "VoxPlay", pm, 1, hDLL

;フェードイン(5秒かけて最小ボリュームから最大ボリュームまでフェード)
pm.0=VoxID, 0, 10000, 5000
dllproc "VoxFade", pm, 4, hDll

;多重再生もサポートしています
msg = "test2.ogg" : getptr pm, msg
dllproc "VoxLoad", pm, 1, hDLL
VoxID2 = dllret
pm = VoxID2 : dllproc "VoxPlay", pm, 1, hDLL

;時間待ち
wait 5000

;ドライバ解放
pm = VoxID : dllproc "VoxRelease", pm, 1, hDLL

;ドライバ破棄
pm = VoxID : dllproc "VoxDelete", pm, 1, hDLL

;多重再生していたドライバも解放
pm = VoxID2 : dllproc "VoxRelease", pm, 1, hDLL
pm = VoxID2 : dllproc "VoxDelete", pm, 1, hDLL

ll_libfree hDLL

HSPコミュニティーの皆様のご利用をお待ちしています(≒バグ出してね)。

03/06/27
追記 HSPスクリプトの修正
03/09/25
追記 ソースをver0.60に更新、めんどくさくなったので(?)vorbisfileを使うことにしました。

Xファイルにユーザーデータな話
実はこんなこともできるんです
Write: 03/04/22 UpDate: 05/03/09

世間には3Dを表現するファイルフォーマットが山のようにあります。
もっともそれぞれ一長一短があり、これぞ最高というものはなかなかありません。
必要に応じて独自のフォーマットを作る事もままあります。
今回は一から作るのは面倒なので、Xファイルにユーザーが定義したデータを追加してみる事にします。
Xファイルではデータ構造をtemplateで定義します。
例えば球を表すテンプレートは

template Sphere {
<89AC6A89-B956-4243-BFE2-1F70CB93A146>
FLOAT XPos;
FLOAT YPos;
FLOAT ZPos;
FLOAT Radius;
}

といった感じで定義することができます。
<>でくくられている怪しげな数字はWindowsで言う所のGUIDになります。
自分で定義したテンプレートを作成する時もこの数字を用意しなければなりません。
作り方は簡単で、VC7を使っている場合は\Program Files\Microsoft Visual Studio .NET\Common7\Toolsに
guidgen.exeというツールがあるので、これを使えば良いですし、フリーウェアでも色々ツールが出ています。

template BoundingSpheres {
<9E1F530E-E6EF-4ea5-9562-5F783288F3E3>
DWORD Number;
array Sphere BoundingSpheres[Number];
}

このようにtemplate中で別のtemplateによって定義されたデータ構造を用いる事もできます。
templateのフォーマットについてはヘルプに詳細が出ているのでそちらを参照してください。
続いて、実際にデータをXファイルに追加します。
追加できる場所は3箇所で、TopLevel、Frame、Meshのいずれかになります。
階層構造で言えばTopLevelの下がFrameで、その下がMeshになります。
例えば、あるFrameにデータを追加するには

Frame Test {
BoundingSpheres{
2;
1.000000;2.000000;3.000000;4.000000;,
5.000000;6.000000;7.000000;8.000000;;
}

FrameTransformMatrix {
...

のように、Frameの中に書き加えるだけです。
最後に、追加したデータをプログラムから読み出します。
DirectX9ではD3DXLoadMeshHierarchyFromXという便利な関数でXファイルを読み下せます。
この関数の第5引数でユーザー定義データを解析する関数を設定できます。
引数はID3DXLoadUserDataのアドレスを要求します。
このインターフェースにはTopLevel、Frame、Meshのそれぞれに追加されたデータが読み込まれた時に呼び出される
LoadTopLevelData、LoadFrameChildData、LoadMeshChildData
の3つの関数が定義されています。
要はID3DXLoadUserDataを継承したクラスを作り、以上の3つの関数を実装して、適宜データを読み出すだけです。
データはIDirectXFileDataを使って渡されるので、メンバ関数のGetTypeによりGUIDを取得し、
GetDataによりデータへのポインタを取得します。
ポインタはvoid*になっているのでGUIDでデータの種類を判定してキャストしながら読み出す必要があります。
以上の方法でテキスト形式のXファイルにユーザー定義データを追加できるのですが、
バイナリ形式だとtemplateをファイル内に定義できないので、プログラム中でIDirectXFile::RegisterTemplatesを使って
テンプレートを定義する必要があります(未確認)。
余談ですが、DirectX9になってもバイナリ形式は挙動不審です。
SDKにあるMesh Viewerでテキスト形式のXファイルを読み込んで、
バイナリ形式で書き出すと、できたファイルが読み込めない事がありました。
どうしてもファイルサイズを小さくしたい場合か、
読み込み速度を上げたい場合以外はテキスト形式を使うのが無難でしょう。
というわけで、今回のソースです。
キャラクタにバウンディング球のデータを追加しています。
外部依存ファイルに対するパスは自分で直してください。

毎度のことですが、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。


03/06/27
追記 DirectX 9.0 SDK (December 2004)版のソースを公開

更新チェッカな話
短いことは良いことだ
Write: 03/04/09 UpDate: --/--/--
野村XXはNetscapeを愛しています。
というかNetscapeのブックマークを、です。
IEのお気に入りの挙動に比べて素直なところが気に入っているんですが、
いかんせんユーザー数が減少の一途を辿っているせいか、
使い勝手の良い更新チェッカが見当たりません。
なら、作ってしまえというのが今回のネタ。
仕様
・Netscape7のbookmarks.htmlからリンク先を抽出 ・リンク先のページを過去に保存したものと比較 ・更新されていたら自動でブラウザを立ち上げる とりあえずこんな所でしょう。
あとはどれだけ手抜きして完成させるかだけです。

まずブックマークから<A HREF="http://...で始まるリンク先を切り出さなければなりません。
strstrとか使って地道にやってもいいんですが、ここはさくっと正規表現に頼ってしまいましょう。
正規表現のライブラリはboostを使用するということで、
使ったことのない方は、こことかこことか参照。
MS万歳な人はATLとかでお茶を濁すのもありでしょう。
とりあえず、たまーに役に立つのでHDDにboostを転がしておくと幸せになれます。
続いて必要はないのですが、せっかくなのでサイト名も切り出します。
Netscape7は文字コードがUTF-8になっているのでs-jisに変換しなければなりません。
今まで文字コードなんて気にもしなかったので、UTF-8 = unicode だと
思い込んでしまって大混乱してしまいました。 分かってしまうと大したことではなく、MultiByteToWideChar->WideCharToMultiByteの順で
APIを呼び出してあげるだけで解決。 そういえばboostの正規表現ってUTF-8に対応しているかどうか謎です。
とりあえず動いているので良しとしておきましょう。
http接続は、ここを参考にWinInetAPIにお任せします。 InternetOpenUrlを使って、ついでにInternetSetOptionでタイムアウト値を
セットしておくのもありでしょう(タイムアウト設定なしだと固まる可能性がある)。
あとはざくざくhtmlを読み込んでファイルに吐き出します。
過去の履歴と比較して更新されていたり、新規リンク先だったりした場合は
ブラウザを立ち上げます。
これはShellExecuteでできます。
2番目の引数に"open"を指定してもURLを開けますが、
複数のブラウザを立ち上げることができない(ひとつのブラウザの表示内容が切り替わるだけ)ので
3番目の引数に実行ファイル名を直接入れて呼び出します。
以上で完成です、んー簡単ですね。
あんまり簡単なのでソースをそのまま張っておきます。
なんとなく今日はC++の気分なので、C++色の強いコードになってます。
と、作ってみたもののカウンタや、バナーのちょっとした変更とかでも
敏感に反応して更新判定されてしまうので、いまいち使いづらいです。
いきなりブラウザを立ち上げるのではなく、一度ユーザーに聞いてあげる方が
親切かもしれません。
実用性は?ですが更新チェッカとしては最短コードの部類ではないかと。
使い方は実行ファイルがある場所にdataというディレクトリを掘ってから、
コマンドラインで引数にブックマークファイル名を指定して起動するだけです。
というかリンク先の入ったhtmlなら何でもOKです。
IEのお気に入りをエクスポートしたファイルでは必要ありませんが、
Netscapeの場合は引数に-UTF8を付けてください。

#include <windows.h> #include <wininet.h> #include <memory.h> #include <iostream> #include <fstream> #include <string> #include <boost/regex.hpp> #pragma comment( lib, "wininet.lib" ) #define MAXSTRING ( 1024 * 10 ) // なんとなく10K
struct URLSTRINGS{ std::string Host, Path, Name; }; int main( char argc, char* argv[] ) { // 引数解析( -UTF8はNetscapeのブックマーク用 あとはファイル名) bool IsUTF8 = false; char FileName[MAX_PATH]; for( int l = 0; l < argc; l++ ){ if( strcmp( argv[l], "-UTF8" ) == 0 ) IsUTF8 = true; else strcpy( FileName, argv[l] ); } // ブックマークからリンクアドレスを抽出 std::ifstream fin( FileName ); if( fin.fail() ) return -1; char Buffer[MAXSTRING]; std::vector< URLSTRINGS > URL; while( fin.getline( Buffer, MAXSTRING ) ){ boost::reg_expression<char> Regex = "(<A HREF=\"http://)([^/]*)([^\"]*)(\".*>)(.*)(<)"; boost::match_results<const char*> Results; if( boost::regex_search( Buffer, Results, Regex ) ){ URLSTRINGS s; s.Host = Results.str( 2 ); s.Path = Results.str( 3 ); if( IsUTF8 ){ // UTF-8 -> Unicode -> s-jis MultiByteToWideChar( CP_UTF8, 0, Results.str( 5 ).c_str(), -1, (wchar_t *)Buffer, MAXSTRING ); char Buffer2[MAXSTRING]; WideCharToMultiByte( CP_ACP, 0, (wchar_t *)Buffer, -1, Buffer2, MAXSTRING, NULL, NULL ); s.Name = Buffer2; } else s.Name = Results.str( 5 ); URL.push_back( s ); } } fin.close(); // httpでリンク先ファイル取得 HINTERNET hInet = InternetOpen( "Tiny Link Checker", INTERNET_OPEN_TYPE_PRECONFIG, "", "", 0 ); if( !hInet ) return -1; for( unsigned int i = 0; i < URL.size(); i++ ){ HINTERNET hConn = NULL, hReq = NULL; bool IsExist = false; bool IsSame = true; hConn = InternetConnect( hInet, URL[i].Host.c_str(), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL); if( !hConn ) goto FINISH; hReq = HttpOpenRequest( hConn, "GET", URL[i].Path.c_str(), "HTTP/1.0", NULL, NULL, 0, NULL ); if( !hReq ) goto FINISH; if( !HttpSendRequest( hReq, NULL, 0, NULL, 0 ) ) goto FINISH; { BOOL Result; // ファイル名にできない文字を_に置換 strncpy( Buffer, ( URL[i].Host + URL[i].Path ).c_str(), MAXSTRING - 1 ); for( int j = 0; j < MAXSTRING - 1; j++ ){ if( Buffer[j] == '\\' || Buffer[j] == '/' || Buffer[j] == ':' || Buffer[j] == '*' || Buffer[j] == '?' || Buffer[j] == '"' || Buffer[j] == '<' || Buffer[j] == '>' || Buffer[j] == '|' ) Buffer[j] = '_'; } // 以前アクセスしたログを調べる std::ifstream fin( ( std::string( ".\\data\\" ) + Buffer ).c_str(), std::ios::binary ); char *pCash = NULL; if( fin.fail() == 0 ){ IsExist = true; fin.seekg(0, std::ios::end); int Size = fin.tellg(); pCash = new char[Size]; fin.seekg(0, std::ios::beg); fin.read( pCash, Size ); fin.close(); } // ログと比較 std::ofstream fout( ( std::string( ".\\data\\" ) + Buffer ).c_str(), std::ios::binary ); if( fout.fail() ) goto FINISH; int Pos = 0; do{ DWORD Size = 0; ZeroMemory( Buffer, MAXSTRING ); Result = InternetReadFile( hReq, Buffer, MAXSTRING - 1, &Size ); if( Size == 0 ) break; if( IsSame && IsExist ){ if( memcmp( pCash + Pos, Buffer, Size ) != 0 ) IsSame = false; Pos += Size; } fout.write( Buffer, Size ); } while( Result ); delete[] pCash; fout.close(); } // ブラウザ起動(IEを使いたい時はiexplore.exe) if( !IsSame || !IsExist ) ShellExecute( NULL, NULL, "Netscp.exe", ( URL[i].Host + URL[i].Path ).c_str(), NULL, SW_NORMAL ); // 結果表示 std::cout << "[" << i + 1 << "/" << URL.size() << "] " << URL[i].Name.c_str() << std::endl; if( !IsExist ) std::cout << "- 新規 -" << std::endl; else{ if( !IsSame ) std::cout << "- 更新 -" << std::endl; else std::cout << "- ---- -" << std::endl; } std::cout << ( URL[i].Host + URL[i].Path ).c_str() << "\n\n"; FINISH: if( hReq ) InternetCloseHandle( hReq ); if( hConn ) InternetCloseHandle( hConn ); } return 0; }

DirectX9のアニメーションな話
進化についていくのも大変です
Write: 03/02/02 UpDate: 03/04/12

さて今回のネタはDirectX9のXファイル関係での変更点の目玉と思われるID3DXAnimationControllerです。
こいつの登場により、DirectX8以前では自分で実装しなければならなかった
Xファイルのアニメーション関係のコードが一切要らなくなってしまいました。
良い時代になったものです。
ただし困ったことに、ヘルプの記述がそっけない事甚だしい。
せっかく良いフレームワークを提供してくれているんだから
積極的に情報をくれてもいいもんなんですけどね。
こちらに素晴らしい資料があるのでありがたく拝見しましょう。
とりあえず、いつも通りSkinnedMeshサンプルをソフトウェアスキニングの部分だけにして
読みやすく整形する所からはじめましょう。
これです
外部依存ファイルに対するパスは自分で直してください。
スペースキーを押すとモーションブレンドしながら動作が切り替わります。
毎度のことですが、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。

こ、これは笑ってしまうほど簡単。
全体的にCOMの匂いが強いので馴染みづらいですが、実にシンプルなコードです。
D3DXLoadMeshHierarchyFromXの引数で指定されるID3DXAllocateHierarchyが
Xファイルを勝手に読み下してくれるので、こちらはFrameとMeshをどのように作成、破棄するかを
指示するだけで済みます。
読み込みが終わると親FrameとAnimationControllerへのポインタを返してくれます。
親子付けのアニメーションならこれで準備完了です。
ワンスキンの場合はMeshがどのFrame(ボーン)に属しているのか設定を行います。
たったこれだけで読み込み終了。
後はAnimationControllerを使ってアニメーションの設定をして描画するだけです。
描画部分は自分でやらなければなりません。
というかここを隠蔽されるとさすがに不便なので当然といえば当然です。
では問題のID3DXAnimationControllerを眺めてみましょう。
とりあえず気が付いたことを適当に整理します。

アニメーションコントローラ :
アニメーションの時間(姿勢)の設定と複数のアニメーションセットをブレンド、
いわゆるモーションブレンディングができるだけです。
実際それ以上の機能は要らないので必要にして十分です。
Xファイルにアニメーションが含まれていない場合は
D3DXLoadMeshHierarchyFromXはID3DXAllocateHierarchyへのポインタにNULLを返すので注意。
CloneAnimationControllerはアニメーションコントローラのコピーを作ってくれますが、
ここで指定する各種設定値の最大数については元のコントローラの最大値を下回ることはできません。
逆に上回る分には構いません。
前記のこちらのサイトを見る限り、安全性に疑問が残ります。

アニメーションセット :
走る、歩く、跳ぶといった動作データひとつずつがアニメーションセット。
LightWaveでは一つのXファイルに一つのアニメーションセットしか書き出せないので、
複数Xファイルをテキスト形式で作成後、 アニメーションセット部分をカット&ペーストするという
涙ぐましい方法でXファイルに複数のアニメーションセットを持たせることができます。
D3DXLoadMeshHierarchyFromXを使った場合、Xファイル中のアニメーションセットを全て読み込んでくれます。
ただしデフォルトでトラックに読み込まれるのは、ひとつだけなので、
複数のトラックにアニメーションセットを読み込むには
GetAnimationSet、SetTrackAnimationSetを使って手動で設定する必要があります。
GetAnimationSetで指定するアニメーションセットのインデックスは
Xファイル中に書かれている順番と逆順になる模様。
手動で読み込んだトラックのD3DXTRACK_DESCには値が設定されていないので
SetTrackDescで設定しないといけません。

イベント :
謎。いくつかのXファイルで試した所、我が家の環境ではGetMaxNumEventsの返値は
常に30を返してくるので、これがデフォルト値らしい。

行列 :
ボックスを20個親子付けしたXファイルを作ってGetMaxNumMatricesの返値を見ると
root+frame数の21を返してきました。
D3DXLoadMeshHierarchyFromXを使った場合
Xファイル中に登場する名前付き行列がID3DXAnimationControllerに登録されます。
現在のXファイル仕様にはFrameTransformMatrix以外の行列が存在しないので
結局FrameTransformMatrixの数がセットされたということのようです。
RegisterMatrixのヘルプが分かりづらいですが、
要はこのFrameTransformMatrixのような名前付き行列を変更するためのものです。
その際のNameにはFrameの名前を指定することになります。

トラック :
アニメーションセット置き場

ミキサ :
複数のトラックをどのように混ぜるかコントロールするもの。
トラックにしろミキサにしろ音楽用語のイメージでOKかと。

グローバルアニメーションタイム:
マスタクロックとでも言うべきものです。
デフォルト状態ではここで設定した時間通りにアニメーションが進みます。
トラック毎に時間を設定することもできます。
ヘルプにはトラック速度の設定関数にSetTrackAnimationTimeがあると書いてありますが、
SetTrackAnimTimeの誤植のようです。

ブレンド :
まず複数のトラックにアニメーショントラックをセットしておきます。
あとはKeyTrackEnableを使ってブレンドしたいトラックを全て有効にするだけです。
フェードアウトするモーションとフェードインするモーションの各トラックのウェイトを
SetTrackWeightで徐々に変化させれば滑らかにモーション間をつなぐことが出来ます。

Priority :
謎、トラック間に優先順位があるらしいのですが、その優先順位を設定するパラメータが見当たりません。
Track1 > Track2 > Track3 > ... と固定されている可能性が高いですが不明です。

と、少しいじった感じではこんな所です。
KeyPriorityBlend辺りは結構便利そうな雰囲気が漂うもののPriorityについて良く分からないので、
もう少し情報を集めないと正しい使い方が分かりませんね。
あと問題点としては、D3DXLoadMeshHierarchyFromXがデフォルトで提供してくれる
アニメーションコントローラがトラックを2つしか持っていない点です。
現状ではコントローラで使えるトラック数を増やすにはCloneAnimationControllerを使うしかありません。
バグがあるかも知れないCloneAnimationControllerをどきどきしながら使う羽目になります。
実際のゲーム製作においては、ブレンド数が瞬間的に2つを超えることがあるので、ちょっと困りモノです。

03/04/12 追記 サンプルソースにリソースのリリース忘れがあったので修正
あと、アニメーションの時間の単位が謎ですが、Xファイル中に以下のテンプレートを
定義すると設定することができるようです。

template AnimTicksPerSecond {
<9E415A43-7BA6-4a73-8743-B73D47E88476>
DWORD AnimTicksPerSecond;
}

AnimTicksPerSecond{
1;
}

LightWaveでワンスキンな話
今回はプログラムと関係なし
Write: 02/11/14 UpDate: 02/11/17

ちょっとお役立ち情報ということで、LightWaveでDirectX用のワンスキンモデルを作るための覚書です。
結構悩まされたので公開しておきます。
といっても重要なのは7番目の項目だけです。

1. ポリゴンモデルを作る
黙々とポリゴンをこねくり回します。
なるべく頂点数を減らした方が後のウェイトマップ設定が楽になります。
もちろん処理も軽くなるのでここは腕の見せ所です。
4角ポリゴン主体に組んでいくのですが、関節部分は3角ポリゴンにしておいた方が曲がったときの破綻が少なくなります。

2. テクスチャを貼る
マテリアルが増えると処理が重くので、なるべくUVマップを利用してサーフェースを増やし過ぎないようにテクスチャを貼っていきます。
ちなみにどれくらい重くなるかというと、同じポリゴンモデルで1マテリアル+UVが700FPS、30マテリアル+UVが70FPSと
実に10倍も差が付いてしまいました。
UVマップ上で頂点を移動させると他の部分もくっついて動く場合は、
一度モデルの頂点を結合解除してからUV上の頂点位置を調節し、
再びモデルの頂点を結合しなおせばOKです。

3. スケルゴンを仕込む
モデラー上でスケルゴンを配置していきます。
続いて親子付け&名前付けをスケルゴンツリーで行います。
LW7.5使っている場合はD-StormのHPに落ちているSkelgonEditorを使う方が少し楽です。
当然ですがスケルゴンの総数が少ない方が処理が軽くなるので、無駄なスケルゴンはなるべく作らない方が吉です。
ここでCalsium2というプラグインを利用させてもらいます。
LW標準では全てのスケルゴンは強制的につながってしまうのに対し、
これを利用する事で親子付けされていながらも位置の離れたスケルゴン(浮動ボーン)を
作れるようになるので、スケルゴン削減に一役買ってくれます。

4. ウェイトマップを行う
全てのボーンにウェイトマップを付けます。
Calsium2を利用する場合は、ダミーのスケルゴンについては後で削除されるのでウェイトマップは必要ありません。
また、全ての頂点はいずれかのウェイトマップの影響を受けていなければなりません。
VertexPaintを使ってウェイト設定すると意図していない頂点にうっかりウェイトをつけてしまったりするので、
頂点を選択してからエアブラシを使って確実にウェイト設定する方が良いかもしれません。
MirrorWeightsとかanon_WSheetといったプラグインが重宝します。

5. レイヤーをまとめる
レイアウトに持っていくときに一つのレイヤーにまとまっていないとスケルゴンがまともに変換されないので、まとめてからセーブします。
非常に不便なので改良してほしいですね。

6. スケルゴンをボーンに変換する
ここからはレイアウトで作業です。
Skelgons to Boneを使うのがLW標準ですが、ここではCalsium2を利用してボーンに変換します。
理由は浮動ボーンが使える事と、ボーン変換後のバンク角がまともであるという事の2点です。
一般オプションの『その場でペアレント』を有効にしてからCalsium2のオプションを
『Skelgonを処理』、『_Fで終わるBone名は浮動ボーンのダミー』にして変換します。
Skelgons toBoneを使った場合のバンク角の修正法については
『LightWave3D ver.7 キャラクターアニメーション』という本で紹介されているのでそちらを参照してください。

7. ボーンのオプションを変更する
これが最重要です。
全てのボーンのオプションで『ウェイトのみ使用』を選択、『ウェイト常態化』は選択をはずします

スプレッドシートを利用して一気に設定するのもありでしょう。
LWのボーンには色々設定項目がありますが、最終的に.X形式で出力したときに反映されるのは
ウェイトマップの値だけと思って間違いありません。
この状態でまともに見えるように、スケルゴンの位置と頂点のウェイト値を調節します。
モデラーとレイアウトを行ったり来たりで大変ですが頑張りましょう。

8. X形式で出力する
D-StormのHPに落ちている最新版のDirectXExporterを使ってX形式のファイルを作ります。
オプションで『Export Animation』、『Specify Max of SkinWeights/Vertex』を選択。
『Max of SkinWeights/Vertex』と『Max of SkinWeights/Face』はケチって小さな値にすると思い通りの出力が得られません。
またDirectX8形式でバイナリ出力したXファイルはまともに読み込めないので避けましょう。
ちなみにLWの両面ポリゴンは片面になって出力されてしまうので要注意です。

以上、これで明日から貴方も素敵なワンスキニスト(?)

02/11/17
追記 ウェイト常態化、マテリアル数、プラグインについて追加

 


戻る