著者名が「magicien
11〜20件目 / 212件
前へ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 次へ

 SceneKitのクォータニオンをMMDのオイラー角に変換する / magicien 

JSceneKitを実装するときに、オイラー角とクォータニオンを相互変換する式を計算したのだけれど、今回はSceneKitのクォータニオンをMMDのオイラー角に変換する必要があった。
が、前回使った計算式はどこかに行ってしまったので、再計算しなければならなかった。今回は途中式を記録に残しておく。

前提

  • orientation(クォータニオン)からeulerAngles(オイラー角)を直接計算するのではなく、orientation→rotation、rotation→eulerAnglesの二段階の変換をする。
  • orientationとrotationの変換は簡単なので省略。計算式は以前書いたSceneKitの系を参照のこと。
  • 今回は回転だけ気にするので、3x3行列で計算する。

計算に使う行列

SCNNode.rotation (Rx, Ry, Rz, Rw) に対応するtransform:
※rotationのxyz軸は大きさ1に正規化される。
\( rotation = (R_x, R_y, R_z, R_w) \\ \Rightarrow \left( \begin{array}{ccc} R_x^2 (1-\cos R_w) + \cos R_w & R_xR_y(1-\cos R_w) + R_z\sin R_w & R_xR_z(1-\cos R_w) - R_y\sin R_w \\ R_yR_x(1-\cos R_w) - R_z\sin R_w & R_y^2(1-\cos R_w) + \cos R_w & R_yR_z(1-\cos R_w) + R_x\sin R_w \\ R_zR_x(1-\cos R_w) + R_y\sin R_w & R_zR_y(1-\cos R_w) - R_x\sin R_w & R_z^2 (1-\cos R_w) + \cos R_w \end{array} \right) \tag{1} \)

SCNNode.eulerAngles (Ex, 0, 0) に対応するtransform:
\( eulerAngles = (E_x, 0, 0) \Rightarrow \left( \begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos E_x & \sin E_x \\ 0 & -\sin E_x & \cos E_x \end{array} \right) \tag{2} \)

SCNNode.eulerAngles (0, Ey, 0) に対応するtransform:
\( eulerAngles = (0, E_y, 0) \Rightarrow \left( \begin{array}{ccc} \cos E_y & 0 & -\sin E_y \\ 0 & 1 & 0 \\ \sin E_y & 0 & \cos E_y \end{array} \right) \tag{3} \)

SCNNode.eulerAngles (0, 0, Ez) に対応するtransform:
\( eulerAngles = (0, 0, E_z) \Rightarrow \left( \begin{array}{ccc} \cos E_z & \sin E_z & 0 \\ -\sin E_z & \cos E_z & 0 \\ 0 & 0 & 1 \end{array} \right) \tag{4} \)

eulerAnglesのxyz回転を全て指定した場合は、SceneKitの場合、Z軸・Y軸・X軸の順番で回転する。回転行列は左側から掛けるので、次のようになる(この式は使わないけど、補足として)。
\( eulerAngles = (E_x, E_y, E_z) \Rightarrow \left( \begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos E_x & \sin E_x \\ 0 & -\sin E_x & \cos E_x \end{array} \right) \left( \begin{array}{ccc} \cos E_y & 0 & -\sin E_y \\ 0 & 1 & 0 \\ \sin E_y & 0 & \cos E_y \end{array} \right) \left( \begin{array}{ccc} \cos E_z & \sin E_z & 0 \\ -\sin E_z & \cos E_z & 0 \\ 0 & 0 & 1 \end{array} \right) \tag{5} \)

MMDのカメラモーション(vmd)のオイラー角をSceneKitで再現する場合は、Z軸・-X軸・-Y軸の順で回転する必要がある。よって、回転行列は次のようになる。
\( MMDeulerAngles = (E'_x, E'_y, E'_z) \\ \Rightarrow eulerAngles(0, -E'_y, 0) \times eulerAngles(-E'_x, 0, 0) \times eulerAngles(0, 0, E'_z) \\ = \left( \begin{array}{ccc} \cos E'_y & 0 & \sin E'_y \\ 0 & 1 & 0 \\ -\sin E'_y & 0 & \cos E'_y \end{array} \right) \left( \begin{array}{ccc} 1 & 0 & 0 \\ 0 & \cos E'_x & -\sin E'_x \\ 0 & \sin E'_x & \cos E'_x \end{array} \right) \left( \begin{array}{ccc} \cos E'_z & \sin E'_z & 0 \\ -\sin E'_z & \cos E'_z & 0 \\ 0 & 0 & 1 \end{array} \right) \\ = \left( \begin{array}{ccc} \cos E'_y \cos E'_z - \sin E'_x \sin E'_y \sin E'_z & \cos E'_y \sin E'_z + \sin E'_x \sin E'_y \cos E'_z & \cos E'_x \sin E'_y \\ - \cos E'_x \sin E'_z & \cos E'_x \cos E'_z & - \sin E'_x \\ - \sin E'_y \cos E'_z - \sin E'_x \cos E'_y \sin E'_z & - \sin E'_y \sin E'_z + \sin E'_x \cos E'_y \cos E'_z & \cos E'_x \cos E'_y \end{array} \right) \tag{6} \)

SceneKitのrotationをMMDのeulerAnglesに変換

rotation(Rx, Ry, Rz, Rw) から MMDeulerAngles(E'x, E'y, E'z) を求める。
rotationとMMDeulerAnglesの回転行列を一致させてあげれば良いので、(1) = (6)より、
\( \left( \begin{array}{ccc} R_x^2 (1-\cos R_w) + \cos w & R_xR_y(1-\cos R_w) + R_z\sin R_w & R_xR_z(1-\cos R_w) - R_y\sin R_w \\ R_yR_x(1-\cos R_w) - R_z\sin w & R_y^2(1-\cos R_w) + \cos R_w & R_yR_z(1-\cos R_w) + R_x\sin R_w \\ R_zR_x(1-\cos R_w) + R_y\sin w & R_zR_y(1-\cos R_w) - R_x\sin R_w & R_z^2 (1-\cos R_w) + \cos R_w \end{array} \right) \\ = \left( \begin{array}{ccc} \cos E'_y \cos E'_z - \sin E'_x \sin E'_y \sin E'_z & \cos E'_y \sin E'_z + \sin E'_x \sin E'_y \cos E'_z & \cos E'_x \sin E'_y \\ - \cos E'_x \sin E'_z & \cos E'_x \cos E'_z & - \sin E'_x \\ - \sin E'_y \cos E'_z - \sin E'_x \cos E'_y \sin E'_z & - \sin E'_y \sin E'_z + \sin E'_x \cos E'_y \cos E'_z & \cos E'_x \cos E'_y \end{array} \right) \tag{7} \) で、E'x、E'y、E'zをRx、Ry、Rz、Rwで表せれば良いという寸法。


まずは E'x から。(7)の両辺の 2行3列目を取ると、
\( R_yR_z(1-\cos R_w) + R_x\sin R_w = - \sin E'_x \tag{8} \)
よって、
\( E'_x = \arcsin{(- R_yR_z(1-\cos R_w) - R_x\sin R_w)} \tag{9} \)

次は E'y。(7)の 1行3列目 / 3行3列目 を計算すると、
\( \frac{ R_xR_z(1-\cos R_w) - R_y\sin R_w }{ R_z^2 (1-\cos R_w) + \cos R_w } = \frac{ \cos E'_x \sin E'_y }{ \cos E'_x \cos E'_y } \tag{10} \)
よって、
\( E'_y = \arctan \frac{ R_xR_z(1-\cos R_w) - R_y\sin R_w }{ R_z^2 (1-\cos R_w) + \cos R_w } \tag{11} \)

最後に E'z。(7)の 2行1列目 / 2行2列目 を計算すると、
\( \frac{ R_yR_x(1-\cos R_w) - R_z\sin R_w }{ R_y^2(1-\cos R_w) + \cos R_w } = \frac{ - \cos E'_x \sin E'_z }{ \cos E'_x \cos E'_z } \tag{12} \) よって、
\( E'_z = \arctan \left( - \frac{ R_yR_x(1-\cos R_w) - R_z\sin R_w }{ R_y^2(1-\cos R_w) + \cos R_w } \right) \tag{13} \)

これでMMDのオイラー角が全て求められた。ちなみに、E'x が直角だと、cosE'x が 0 になり、(10)、(12)の右辺分母が0になってしまう。ので、別の方法で計算しないといけない(計算方法を忘れたので続きはまた今度)。
E'xが直角だと何が起こるかというと、 Z軸・-X軸・-Y軸の順で回転させた結果、X軸回転後にY軸がもともとZ軸があった場所に来てしまう。Y軸を回してもZ軸を回しても結果は同じ、ということで答えが一意に定まらなくなる。

2017/12/20(Wed) 03:53:33

 MMDSceneKitに物理演算を追加した / magicien 

image 寝癖すごいことになってますよ?

作ろうと思って放置していた物理演算を追加した。pmd、pmx単体で読み込む分には、剛体が正しく読み込まれるはず。モデルをcloneした時の処理が未修正のため、pmmは今のところ未対応。

問題は、SceneKitにbtGeneric6DofSpringConstraintに該当するSCNPhysicsBehaviorが存在しないこと。代わりにSCNPhysicsBallSocketJointを使ってみたけれど、回転角度に制限が入れられないので、髪がねじれ放題。そして、なぜか前髪もあらぬ方向へ。
SceneKitは明らかにBullet Physicsを使っているので、6DoFのAPIが裏に隠れているはず。ぜひ使わせて欲しいのだけれど、なぜか少しずつ小出しにされるので、まだ使えません!
場合によっては見た目が残念なことになるので、MikuMikuDanceQuickLookに物理演算を組み込むのは、まだやめておこう。

別途Constraintを追加してあげれば良いのかなぁ...そうするくらいなら、自分でBullet Physicsを組み込む方が早い気がする。

2017/12/18(Mon) 06:46:05

 MikuMikuDanceQuickLookの動画を作った / magicien 

ライブラリ公開と併せて作るはずだった動画をようやく投稿できた。



Homebrewを使ったインストール方法を用意できると良かったんだけど、Githubで公開しているものをHomebrewにも登録する場合、リポジトリのスターが10以上あることが条件になっているので、登録できなかったのだ!
このプラグインに興味を持ってくれた人は、今すぐMikuMikuDanceQuickLookのリポジトリにスターを付けるんだっ!

2017/12/10(Sun) 19:55:36

 羽生竜王+永世七冠おめでとうございます! / magicien 

永世七冠、タイトル99期なんてもはや棋士としてカンストしてしまっているけれども、これからのご活躍も応援しています!


それにしても、歴史的瞬間を生中継で見られて本当に良かった。インターネットの発展に感謝せざるを得ない。
こうなると叡王戦が蛇足感あるけど、将棋界にとってはタイトルが増えるのは喜ばしいことなんでしょうね。いずれ羽生さんが永世叡王を獲るだろうしな!

そういえば、先日ニコニコ動画がボコボコにされていたけれど、相撲を生中継してみたり、将棋を盛り上げたりと、日本の文化の発展に寄与しているところが少なからずあると思うので、期待の裏返しだと思って、めげずに頑張って欲しいですね!

2017/12/05(Tue) 18:10:39

 MMD用QuickLookプラグインを作った / magicien 

imageMMDはMikuMikuDanceの略です。念のため。
MikuMikuDanceQuickLook - macOS QuickLook plugin for MikuMikuDance files

最初はMMDQuickLookっていう名前にしようと思っていたけれど、MultiMarkdown用プラグインと名前が被ったので変えました。
プラグインを使っていると、CPU使用率が高いまま張り付く現象が確認されています。OSを再起動するか、
kill -3 <QuickLookUIServiceのPID>
で収まるんだけど、PID間違えると大変なことになるし、その後の動作に悪影響が出る可能性があるので、コマンドの意味が分かる人だけ自己責任でお願いします。
※以前、kill -9と書いていたけれど、kill -3(SIGQUIT)でも終了できるみたいなので修正しました。こっちの方が幾分か安全かな。
原因はQuickLookUIServiceのバグを疑っているけれど、その前に色々と検証せねば。

インストール方法

  1. GitHubのReleasesからzipファイル(MikuMikuDanceQuickLook_vX.X.X.zip)をダウンロードする
  2. zip内の MikuMikuDanceQuickLook.qlgenerator を /Library/QuickLook (全ユーザ用)か ~/Library/QuickLook(個別ユーザ用)にコピーする
  3. ターミナルで、qlmanage -r コマンドを実行(あるいは、OS再起動)して、QuickLookプラグインのリロードを行う。

対応フォーマット

  • PMD
  • PMX
  • X
モーション系のファイルを表示するには、モーションを適用するためのデフォルトモデルをプラグインに同梱してあげる必要があるんだけど、配布しても問題無い軽量なモデルはあるだろうか。

使い方

Finderでファイルを選ぶと、プレビュー欄に3Dモデルが表示されるよ!
ファイル選択中にスペースキーを押すと、大きなプレビュー画面が表示されるよ!
※マウスでモデルを動かせるよ!

2017/11/27(Mon) 07:44:15

 Xcodeのシーンエディタでシェーダのデバッグ中 / magicien 

image GLTFQuickLookについては、既に試していただいた方がいるようで、ありがたい限りです。
が、本当に作りたいのは、MMD用のQuickLookなのです。

読み込んだモデルを一旦scnファイルに書き出す、という作戦に気づいた結果、シェーダのデバッグがしやすくなった。
シーンエディタを使えば、シェーダの編集結果がリアルタイムに反映されて、エラーや警告のある行も表示してくれる。
剛体の角度がうまく設定できていないな...というのも一目瞭然。

glTFと違って、MMDは対応する必要のあるファイルフォーマットがたくさんあって腕が鳴るぜぇ。

2017/11/24(Fri) 20:07:17

 glTF用QuickLookプラグインを作った / magicien 

image QuickLookプラグインの作り方が分かったところで、早速glTFビューアを作ってみた。
GLTFQuickLook - macOS QuickLook plugin for glTF files


インストール方法

Homebrewでインストールする場合

ターミナルで、
brew cask install gltfquicklook
を実行する

手動でインストールする場合

  1. GitHubのReleasesからzipファイル(GLTFQuickLook_vX.X.X)をダウンロードする
  2. zip内の GLTFQuickLook.qlgenerator を /Library/QuickLook (全ユーザ用)か ~/Library/QuickLook(個別ユーザ用)にコピーする
  3. ターミナルで、qlmanage -r コマンドを実行(あるいは、OS再起動)して、QuickLookプラグインのリロードを行う。

使い方

Finderでファイルを選ぶと、プレビュー欄に3Dモデルが表示されるよ!
ファイル選択中にスペースキーを押すと、大きなプレビュー画面が表示されるよ!
※あくまで静止画なので、ドラッグしてもモデルは動かせないよ!
※マウスでモデルを動かせるよ!

開発メモ

QuickLookプラグインでSwiftのフレームワークを使うには、プラグインにSwiftの標準ライブラリを同梱してあげる必要があるようだ。Xcodeで Build Settings > Build Options > Always Embed Swift Standard Libraries を Yes に設定すればOK。
また、追加したフレームワークが、パッケージの Resources というディレクトリに入ってしまうようなので、Build Settings > Linking > Runpath Search Paths に @loader_path/../Resources/ を追加した。

OS標準のscn用QuickLookだと、モデルをグリグリ動かせるんだけど、どうやっているのかなぁ。
scnファイル形式で書き出してQuickLookのAPIに丸投げしたら動いた!
        GLTFSceneSource *source = [[GLTFSceneSource alloc] initWithURL:(__bridge NSURL*)url options:nil];
        SCNScene *scene = [source sceneWithOptions:nil error:nil];

        NSData *scnData = [NSKeyedArchiver archivedDataWithRootObject:scene];
        CFStringRef contentTypeUTI = CFSTR("com.apple.scenekit.scene");
        
        QLPreviewRequestSetDataRepresentation(preview, (__bridge CFDataRef)(scnData), contentTypeUTI, options);

2017/11/19(Sun) 07:20:37

 SwiftのフレームワークをObjective-Cで使えるようにするには / magicien 

各クラスの前に @objcMembers を付けるだけ!
ただ、[MyClass?] みたいに、OptionalのArrayはObjective-Cに変換できないようだ。
ファイルサイズが大きくなってしまう点も気にかけておこう。


image ファイルのプレビュー機能である QuickLook のプラグインは、CかObjective-Cで書かなければならない。プラグインがカーネル側で動くことに関係していそうだが、理由はいまいちよくわからない。
MMDSceneKitはSwiftで書いているので、MMD用のQuickLookを作るには、フレームワークをObjective-Cで書き直さないといけない、と思い込んでいたけれど、@objcMembers を付けたら、Objective-Cからでも問題なく使えた。

モデルが赤紫色で表示されるのは、シェーダでエラーが発生したとき。別ファイルのテクスチャが読み込めていないのかもしれない。いずれにせよ、QuickLook用プラグインでSwiftのフレームワークが使えることに気づいたのは、自分にとって大きな収穫だった。

そういえば、SCNSkinnerのバグレポートはサポートから返信があった。次のバージョンでは直っているといいなぁ。

2017/11/18(Sat) 04:49:22

 JSceneKitでobjファイル表示対応した / magicien 

image Polyなるサービスが始まったらしいと聞いて、JSceneKitで作りかけだったobjファイルの表示機能を実装した。例によって必要最低限の機能しか実装していないけれど、Polyからダウンロードできるobjファイルは概ね表示できるはず。


Polyにアップロードされているモデルを眺めていると、明らかに毛色の違うものがちらほら。どうやらVRで作られたものらしい。3Dモデルというと、直方体やカプセルのような形から始めて、ポリゴンを分割したり、凹凸を付けたりしていくのが一般的だと思うのだけれど、VRで作られたモデルは、まるで油絵のように、色の付いたポリゴンをいくつも重ねて作られていた。ポリゴン同士が連結していないので、隙間から向こうが見える。

露骨な三角形や四角形の組み合わせも折り紙のようで嫌いではないけれど、輪郭の曖昧なモデルも味があって良い。もちろん、VRを使わなくても同じ表現はできるのだろうが、VRがあるからこそ生まれた表現だろう。

2017/11/11(Sat) 04:59:09

 色々と手詰まり中 / magicien 

ここのところ色々なバグや問題に引っかかってあらゆるものが停滞している。以下、駄文。

iOSでclassの変数にアクセスすると、EXC_BAD_ACCESSエラーで落ちる

JSONDecoderで取得したデータにアクセスしようとすると、エラーで落ちる問題があって困惑している。
考えられる原因としては、
  • MainThread以外でJSONDecoderを使っているから?
  • Codableなclassをextendsして使っているから?
  • ARCの処理が変わった?どこかをweakとかunownedとかにしないといけないのか?
  • JSONDecoderかARCのバグ?
といった感じなのだけれど、よくわからない。コールスタックを見ると、こんな感じの見知らぬ関数が呼ばれていた。

  • materializeForSet
  • swift_beginAccess
  • insert
  • reportExclusivityConflict
JSONDecoderで取得したデータにアクセスした時に、排他アクセスチェックが行われ、問題があったのでデバッガにリポートを送信した帰りに変なメモリにアクセスして落ちているようだ。実際は、reportExclusivityConflictが終わり、insertから帰る寸前で何かをやらかしている。スタックに退避したデータをレジスタに書き戻す直前に何かごにょごにょやっているようなのだが、処理を追う気力が無かった...
まぁ元々の問題はどこかで変数書き込み処理がコンフリクトしていることらしいので、それを解決すれば、この問題も自ずと解決するはず。せめて、reportExclusivityConflictの出力がデバッガで受け取れれば良いのだが、その前にエラーで落ちてしまっているようで、原因の究明が難航している。

SCNSkinnerが使えなくなった

ベータ版を使っていた時は大丈夫だったのに。
SCNSkinnerを使うには、boneIndicesとboneWeightsを渡す必要があるのだが、boneIndicesにいつも通りUInt16のデータを渡すと、
[SceneKit] Error: Compiler error while building render pipeline state for node "(null)" (0x1016db2c0): Error Domain=CompilerError Code=2 "Vertex attribute skinningJoints(4) of type uint4 cannot be read using MTLAttributeFormatShort4" UserInfo={NSLocalizedDescription=Vertex attribute skinningJoints(4) of type uint4 cannot be read using MTLAttributeFormatShort4}
というエラーが出る。uint4は4バイトのunsigned intを4つセットにしたベクタ。要するに、2バイトじゃなくて4バイトのデータをくれ、ということなので、UInt32に変換してデータを渡してみると、
[SceneKit] Error: SCNSkinner: bone indices must be uint8 or uint16 (maximum of 2 bytes)
boneIndicesは最大2バイトまでだという。何を言っているのか。
SceneKitに渡せるのは2バイトまで。SceneKitの先にあるMetalに渡す必要があるのは4バイト。詰んだ。

追記:
GLTFQuickLookを作った時に偶然発見したんだけど、NSKeyedArchiverでSCNSceneをNSDataに変換した後、SCNSceneのイニシャライザにデータを渡したら、SCNSkinnerの問題を回避できた... とりあえず、バグが直るまではこれで対応しよう。

その他バグ

以前、いくつかのバグを報告をしたところ、1件だけ反応があり、最新のベータ版OSで再発するか試してくれ、と書いてあったので試したけれど、挙動が変わったものの問題は解決していなかった。報告用のログを集めるのが面倒なのと、ベータ版を使い続けるのが嫌になってダウングレードしてしまったので、返事をせず放置していたら、バグレポートがクローズされてしまった。残りのレポートは応答なし。バグを報告するのが億劫になってきた。でも、SCNSkinnerについてはなんとかしないと先に進めないので、適当に書いて送ろう。
2017/10/28(Sat) 05:39:06