はじめに
のエントリです。SLに2006年の12月に登録して、もう長い月日が流れました。明日がRezdayのようです。 なんとなく、浅く長く関わっています。
今年もあんまり体調が良くなくて、ほとんどlibopenmetaverse関係はいじっていなかったのだけど、この季節だし、ちょっといじった時のまとめもしておきたいので、記しておきます。
- LibOpenMetaverse | Open Metaverse Foundation
- Radegast Metaverse Client · Lightweight client for connecting to Second Life and OpenSim based virtual worlds
でメインで活躍されているLatif Khalifaさんもあんまり体調が良くないようです。 openmetaverse.orgのドメインを更新できなかったようで、他のところに取られてしまったようです。 なお、現在はopenmetaverse.coになったようです。
まぁ、やりたいことをやるには、体は大切ですってことです。
内容についてはタイトル通り、libopenmetaverseを使用してSIMのPacelmapを取得し、描画するというものです。
もう一つ書く予定でしたが、ちょっと無理なのでキャンセルしてしまいました。。。
今いるSIMのPacelMapを取得して、描画
それでは、
- ParcelMapの取得
- 取得したデータの描画
の2つに分けて説明していきます。
ParcelMapの取得
ParcelMapの取得についてはログインした状態で
Client.Network.CurrentSim.ParcelMap;
ParcelMapプロパティにあるデータを取得します。
ドキュメントによると
Provides access to an internal thread-safe multidimensional array containing a x,y grid mapped to each 64x64 parcel's LocalID.
とあり、
public int[,] ParcelMap { get; }
http://lib.openmetaverse.co/docs/trunk/?topic=html/P_OpenMetaverse_Simulator_ParcelMap.htm
4mx4mのParcelのLocalIDがSIMのx座標、y座標として格納されているようです。
基本的には、今いるSIMのPacelMapのデータを取得する場合はこれだけでいいのですが、場合によっては、PacelMapのデータが満ちていない場合があるようです。
そのため、
Client.Network.CurrentSim.IsParcelMapFull()
http://lib.openmetaverse.co/docs/trunk/?topic=html/M_OpenMetaverse_Simulator_IsParcelMapFull.htm
IsParcelMapFull()メソッドの戻り値でチェックします。
PacelMapのデータが満ちていない場合は、
Client.Parcels.RequestAllSimParcels (Client.Network.CurrentSim);
データをリクエストするRequestAllSimParcelsメソッドを実行します。
リクエストすると
Client.Parcels.SimParcelsDownloaded
http://lib.openmetaverse.co/docs/trunk/?topic=html/T_OpenMetaverse_SimParcelsDownloadedEventArgs.htm
SimParcelsDownloadedイベントでSimのParcelのデータが取得できますので、上述したIsParcelMapFull()を見て取得します。
データ取得部ソースはこんな感じです。
if(Client.Network.CurrentSim.IsParcelMapFull()){ ParcelMap = Client.Network.CurrentSim.ParcelMap; } else { SimParcelsDownloaded = new AutoResetEvent (false); Client.Parcels.RequestAllSimParcels (Client.Network.CurrentSim); SimParcelsDownloaded.WaitOne (); }
描画
描画については、今回はXamarin.Androidでの実装による表示です。 CanvasとPaintを使い、DrawLineのメソッドで線を引いています。 環境ごとに実装する際のUI側の描画方法に合わせることになります。
取得したPacelMapのデータを使い、LocalIDが異なるところを境界として、描画したいので、上下左右と比較して格納されている値と異なる場合に一線を引くということで描画していきます。
注意点としては、
- PacelMapは4m*4mなので、SIMの座標と合わせる場合は4倍する必要があります。
- canvasの座標0,0は左上で、配列に格納されているのは左下です。考慮せずcanvasに描画すると270度異なります。
実装においては256x256のcanvasを用意して、配列をx,yを0から63まで順に見て4倍にしながら描画し、270度回転させています。 なお、描画の方向と配列に格納されている順を考慮し線を引くことで、回転させずにもできると思います。
描画部ソースはこんな感じです。
//画像の準備256x256 ParcelLine = Bitmap.CreateBitmap(256,256,Bitmap.Config.Argb8888); Canvas PicDraw = new Canvas (ParcelLine); Paint EdgePaint = new Paint (); EdgePaint.Color = Color.Black; EdgePaint.SetStyle(Paint.Style.Stroke); for(int i = 0;i <= 63;i++){ for(int j = 0;j <= 63;j++){ if(i != 63){ //左右での判定 if(ParcelMap[i,j] != ParcelMap[i+1,j]) { //縦線 PicDraw.DrawLine((i+1)*4,(j+1)*4,(i+1)*4,(j+1)*4-4,EdgePaint); } } if(j != 0){ //上下での判定 if(ParcelMap[i,j] != ParcelMap[i,j-1]) { //横線 PicDraw.DrawLine(i*4,(j+1)*4-4,(i+1)*4,(j+1)*4-4,EdgePaint); } } } } //配列に格納されているものとMAP上に表示する座標が270度違うので回転 Matrix mt2 = new Matrix(); mt2.PostRotate(270.0f); ParcelLine = Bitmap.CreateBitmap(ParcelLine,0,0,256,256,mt2,true); RunOnUiThread(() => ParcelMapView.SetImageBitmap(ParcelLine));
境界線として外枠が描画されてしまうため、i=63,j=0についてはあえて、線を引かないようにしております。 はじめにで表示されている画像がAkibaSIMで実行した際の画像です。
更に
友達リストに入っている方がオーナーの区画を塗ってみました。
これは、ParcelMapプロパティで得られるLocalIDを使って、OwnerIDを得て、フレンドリストのUUIDと比較し、フレンドリストに含まれる場合はそのParcelを塗りつぶしたあとで、境界線を描画しています。
また、SIMのminimapにかぶせてみました(Primary)。
これは、上述の友達リストに入っている方の区画を塗りつぶすのに加えて、
の方法で取得したSIMのminimapの上に描画しています。 なお、jpeg2000からbitmapに変換していますが、CSJ2Kではなく
において説明した手法で実装しています。
他にも3年ぐらい前に書いたSIMにいるアバターの位置の描画を境界線のみ描画したものに合わせてみるのもいいかもしれません。
まとめ
Viewerの内部はこんな感じって言うのを知ってもらえればと思っています。 ここ数年いつもまとめでViewerの公開の話をするのだけど、やる気がなくてこの時期だけちょっと進める的なことをやっていたら、かなりの年月が過ぎていますね。 libopenmetaverseをAndroidできちんと動くようにしたのが4年近く前だったりする。。。
やる気があるときだけできれば、それでいいのかもしれないですね。 投げやりでうまくまとめられません。。。