最近ベクタ地図化したYahoo地図の中の人が、OpenGLを使ったAndroid版地図描画エンジンの入門を始めました。
とりあえずネイティブの面白そうなコードを見るとXamarinに写経したくなるので、Xamarin.Androidで写経した上で、Xamarin.iOSでも動くクロスプラットフォームにしてみました。
https://github.com/tilemapjp/map-android-samples
iOS側はかなり実装適当ですが…。
とりあえず1対1写経してやったあと、iOS側に移そうと思っていろいろ調べてたら、各プラットフォームの標準のOpenGLラッパはなんか結構使用感が違うみたいで、OpenGL初心者の私には何が何やらの状態…。
でもXamarin.iOSのOpenGLサンプルをつらつら見てると、よくOpenTKというライブラリを読み込んでいるので、なんぞこれ?と思って調べてみると、OpenGL、OpenCL、OpenALあたりを共通のAPIで使えるようにc#でラッパしたインタフェースのようでした。
これのOpenGLクラスがXamarin.iOS/Android両方に共通I/Fのライブラリとして提供されていて、かつ割とAndroidのOpenGL I/Fと1対1で置き換えられるので、
- まずAndroidのOpenGL APIを全部OpenTKのそれに置き換えてやる
- (OpenTK自体のAndroid依存を除く)Androidに依存しないOpenTKだけで完結する処理、またユーザ操作を受けて表示領域を切り替える処理等を分離
- Xamarin.iOSでOpenGLの表示領域を準備するところまでMonoTouch.GLKit等で行い、後はAndroidから切り出したOpenTKベースのコードを読み込んでやる
=> 表示できる事を確認 - Xamarin.iOS側のユーザジェスチャ受け取り処理等を、切り出したユーザ操作処理と結びつけてやる
=> 操作できる事を確認
ぐらいで、知識なさ過ぎてちゃちゃっと簡単にレベルではなかったものの、まあ年賀状を準備するのが年末最終日にズレ込む程度の実害で対応できました。
OpenGL、Android、iOS全てに詳しい人であれば、OpenGLの各APIや利用シーケンス等も全部覚えていて、AndroidだろうがiOSだろうが最適なOpenGL対応コードをちゃっと書けてしまうのでしょうが、
OpenGLについてほとんど知識のない人間でも、共通APIになっている部分を移し替えるだけで動いている、しかもネイティブアプリとして、という状態に持っていけるのはすごいのではないでしょうか。
(そんなパッと持ってきたコードを実戦で使いたいかどうかは別として、そういう事もできるポテンシャルというのは評価されてよいと思います。)
いくつか同様の事を試される方のために注意点を。
OpenTKのライブラリロード時にプラットフォーム依存部分がある
ライブラリをusingする時に、各プラットフォーム別に呼び出し分けが必要な部分があるので、注意が必要です。
下記のような感じで共通化できるでしょう。
# if __ANDROID__
using OpenTK.Platform.Android;
# elif __IOS__
using OpenTK.Platform.iPhoneOS;
# endif
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.ES20;
VertexAttribPointer関数の最終引数はIntPtr型
VertexAttribPointer関数の最終引数は、頂点行列の中の読み込みオフセットを指定する値らしいのですが、intではなくIntPtr型です。
Android側APIだと、0指定にお気軽に定数が書いてあったりしたのですが、OpenTKだと0を指定したくても下記のようにする必要があるようです。
int a = 0;
GL.VertexAttribPointer (mAPosition, 3, All.Float, false, FSIZE * 3, (IntPtr)a);
GetAttribLocation関数の引数型がAndroidとiOSで違う
GetAttribLocation関数の第2匹数nameは、実装ミス?なのか何か判りませんが、iOS版ではstring, Android版ではSystem.Text.StringBuilderになっています。
ですので下記のような実装分けが必要です。
# if __ANDROID__
mAPosition = GL.GetAttribLocation (program, new System.Text.StringBuilder("a_Position"));
# elif __IOS__
mAPosition = GL.GetAttribLocation (program, "a_Position");
# endif
行列処理とかの便利関数は、独自実装なので使い勝手が違う
行列式等は、例えば4x4行列なら、Android APIはfloat[16]で処理しているのに対し、OpenTKはMatrix4のような構造体を用意し、回転、射影等の処理関数もそれぞれのデータ型に応じたものが提供されているので、そのまま丸写しという感じにはいきません。
といいつつ、OpenGL固有の関数に比して、の話で、実際は割と丸写しでしたが。
個人的には、Matrix4構造体は専用の型を持つ分float[16]より見通しよくなりそうなイメージがありつつ、実際はmatrix[0][1]みたいなアクセスができるわけではなくmatrix.M11みたいなまさかのプロパティ名アクセスなので、ループとかの自動化処理がやりにくくちょっと使い難そうな感じでした。
結論
やっぱりXamarin、OpenGLも大丈夫ー
誰か
WindowsPhoneでも共通部分そのまま持ち回せるか試してくれないかな…。
うちには環境がない…。