2016年8月31日水曜日

JA04HP(AW139)、JA119L(ベル412)、UH-1、JA7907(R44)、JA001H(SA365)


0916頃


1022頃



1025頃


1049頃



1053頃


1058頃


1102頃

北海道警察のヘリが飛んでた。
その後で札幌市消防局防空隊のベル412が飛んでいた。ここは札幌から遠く離れた上富良野町だけども。

UH-1はよく見る機体だけど、今回の機体はノーズ左と胴体右に何かポッドを積んでる。偵察用の機体かな?情報収集に使ってるのかも。

R44は初めて見たかも。ちょっと感動w。あと変な音がする。ガソリンエンジンだから?安いヘリってこういうもんなのかな。

SA365のJA001Hは見覚えのあるレジ。いつか忘れたけど見た記憶がある。


台風一過であちこち浸水したりしてるせいか、いろんな機体が飛び回ってる。

2016年8月30日火曜日

Bitmapの高速アクセス

C#のBitmapでGetPixelとかSetPixelをやるとかなり遅いので、直接メモリアクセスするライブラリ。

本家ソースコードはこちら
NonSoft - Bitmap処理を高速化するサンプル(C#.NET)

ただしこのコードは24bppRgbアクセスのため、透過情報が破棄されてしまう。ということで32bppArgbに書き直している。ほとんどコピペだけど。

あと色情報をbyte配列で受け取るメソッドを追加してある(与えるメソッドは無い)。

このクラスを使用する場合はSystemとSystem.Drawingの名前空間が必要です。

2016年8月23日火曜日

れんだりんぐえんじん(あるいはXファイルパーサ)



やっとXファイルを適切に読めるようになってきました。といっても内部処理が変わるだけで、結果には何も影響を与えない(与えてはいけない)ので、見た目に変化はありませんが。あ、背景色は変わってますが。

中途半端なXファイルのパーサを作るだけに丸4日くらいかかったのかな。さすがに掛かり過ぎじゃないかとorz

今回作ったものはテンプレートもXファイル内のモノを利用できるので、ちゃんとしたXファイルなら意味を知らないテンプレートが出てきてもちゃんと読めると思います。最も、それを利用するためのコードにはメンバ名等を適切に設定する必要がありますから、あんまり意味ないんですけども。

今回はパーサを作るのが初めてということもあって、かなり妥協してぐだぐだになってるので、もうちょっと綺麗に処理したいなーとか思う反面、もう触りたくないとも思ったり。


段々とPCソフト作りは飽きてきたので組み込みに戻ろうかなとか考えたり。そもそも簡易3D表示はIMUの動作確認用に作り始めた気が。でもマイコンの方もかなり苦労することになると思うので、ちょっと手が出ないです。気力が足りない。


後何日かしたらPCとも組み込みとも別件の部品がいくつか届くはずなので、まずはソレを優先かな。そっちもそっちで苦行の道っぽいけど。

2016年8月22日月曜日

型を指定するHashtable

C#にはHashtable(連想配列)があって、コレを使い時があるけど、HashtableはKeyもValueもObject型なので使い勝手が悪い。ということでKeyとValueの型をそれぞれ指定できるラッパーを作ってみた。
一部のメソッド等は実装しただけで動作確認していないので動かなかったらごめんなさい。例外とかもちゃんと作ってないです。
連想配列とか使いたくなるとJSとか便利だなーとか思ったり。

追記:2016/08/29
.NetFW4以降ならConcurrentDictionaryを使いましょう。こいつならKetとValueの型を指定し、スレッドセーフでアクセスすることができます。

2016年8月20日土曜日

(小ネタ)NEW GAME#7

*モデルガン・エアガン
FN Five-seveN
H&K UMP
スプリングフィールド M14
ベネリ M4
-拳銃数種


*細々
12ゲージ?ショットシェル(発砲済み)
AMMO BOX
迷彩布
-拳銃型水鉄砲
-その他いろいろ

ところで発砲済みの空薬莢はamazonで売ってたり、沖縄の放出品ショップで買うことができるが、例外的に12ゲージのショットシェルは山で拾うことができるらしい。ウチの親が拾ってきた。狩猟で使ったのが落ちてるらしい。まぁ広い山の中でエンカウントするのは相当にリアルラックが必要だろうが。それでも、競技射撃にも使用されるから、入手性はダントツかも。


BB弾小袋の自販機イイね。0.2g1600発600円、0.25g950発600円、0.2g蓄光1000発1200円、位で売って。でもコイン選別的には500円1枚でやれると楽なんだよな、前者2個は1000円、蓄光は1500円でどうだろう。暴利だな。


New Gameは魅力的なキャラが多いけど、褐色さんはかなり好み。

2016年8月19日金曜日

れんだりんぐえんじん(Xファイルを読む)

「れんだりんぐえんじん」って、「えれくとりっく・えんじぇぅ」っぽい語感ですよね。カタカナをひらがなで書いただけですけど。

さて、表題の通り、Xファイルを読み込むようにしてみました。

ゲームプログラマーを目指すひと Xファイルを解析してみる
Xファイルの読み込みは↑のブログを参考にしました。分かりやすかったです。

今回は頂点とマテリアル、法線だけしか使わないので、てきとーにやっつけ読み込みです。まぁメタセコイアから出てくるXファイルってほぼ頂点、マテリアル、法線だけですけども。

今回サンプルデータを作るに当たり久しぶりにメタセコイアを使いました。と言っても新しくなった方ではなく、古い方(Ver3)ですが。
Ver3は昔触ったことがあった事、Ver4でXファイルを出力するにはライセンスを購入する必要が有ること、等が理由です。



とりあえずキューブを表示してみました。
Xファイルでは面に色を指定できるので、ソレで色をつけています。なんか途中で間違ってる気もしますが、とりあえずそれっぽい色になってるのでいいかなと思います。

それと法線を使用して、手前を向いているポリゴン以外は表示しないようにしました。これにより奥側の面は非表示になりますから、深度を使った判定は必要なくなります。もっとも、こちらを向いているポリゴンが2枚以上重なった場合の動作はよろしくないですが、単純なキューブやスフィアなら問題ないと思うのでこの方式で行こうと思います。


単純ではないモデル、例えばキューブが2個並んでいるような形状の場合は以下のようになります。





上の画像は正しい状態ですが、下の画像は反対から見た図で、本来は奥にあるモデルが後に描画され、手前側に表示されてしまっています。
2個別々のモデルでなくても、単純なキューブにちょっとした突起を作っただけでも同様の問題が発生します。ポリゴン同士の組み合わせで「谷折り」となる部分があると、その辺りで問題が発生します。


モデルの色については、現在のところ1面辺り1色としていますが、Graphicsのグラデーションをうまく使えば頂点カラーのような機能も作れるかもしれません。そうすればもうちょっと表現力が上がるかも。どっちにしろ箱や球しか表示できないわけですが。
それでもポリゴンを適切に割って色をつければ文字等も表示できますから、IMUの動作確認に箱を1個表示し、軸がわかりやすいように文字を表示する、くらいはできるかもしれません。
もうちょっと発展の余地有りという感じです。

2016年8月18日木曜日

C#でレンダリング(深さ情報を扱う)




上が前回のレンダリング結果、下が今回のレンダリング結果。
データは同じだが、見た目的には黒の輪郭線の太さを1から3へ太くしたこと、1面毎に色を設定したことが違いとなる。

表示結果は似たようなものだが、プログラムとしては全く別物となってしまった。

まずBitmapの使用を増やしたこと。
前回はBitmap1枚にどんどん書き加えていたが、今回は出力バッファの他にDepthを管理するための画像や、現在のポリゴンのレンダリングバッファと、その深さを描くためのバッファを用意した。
前者2枚はレンダリング開始から終了まで常に開きっぱなしだが、後者2枚についてはポリゴン1個毎に動的に生成している。

深度管理の画像は予めColorと深さ値(今回はintを使用し24bitで管理)を相互に変換する関数を作成しておく。次に出力Depthを最遠でクリアしておく。
ポリゴンを書く場合は画像1枚にポリゴンの画像を、もう1枚にDepth値を書き込む。
その後でDepth値と深さ値画像を比較し、Depth値が深い場合はアルファを0にして透過させ、すべてのピクセルでこの処理を行った後に、バッファとDepthをそれぞれDrawImageで出力バッファに書き加える、という処理になる。

Depth値は1ピクセル毎に計算しているのではなく、ポリゴンの各頂点の平均値を使用している。そのため1ピクセルごとの位置を計算する手間は必要ないが、それでも上の画像を作るだけでかなり時間がかかる(0.8秒程)。
一般的なレンダリングエンジンではGPUの数千コアで並列処理するが、CPUの1コアだけで処理しているために非常に時間が掛かる。

400x400ピクセルの画像で、6面を表示し、辺も含めると12枚の画像となる。それぞれレンダリングとDepthがあるから、24枚の画像をforネストで操作していることになる。それでも3.84Mピクセル程度なので2000x2000ピクセルの画像1枚分程度なのだけど。



このくらいの処理であればCUDAで書くのはそんなに大変ではない気もする。ウチのグラボはGeF 970なので、1000コアくらいで並列処理できるのかな。
でもそこまでやるならレンダリングエンジン探してこいよ、という話になってしまう。

とりあえず、平行投影とはいえ前後に重なったポリゴンを適切に表示できるモノを作っただけでも満足かな。

C#でれんだりんぐえんじん



ちょっとFormで3Dを表示したくなったので試してみた。
とりあえずXNAのVectorとかQuaternionを使えば簡単に座標変換ができるので、平行投影ならサクッと作れる(それでも結構なソースになるが)。

ただ、あくまで座標変換して書き重ねてるだけなので、後ろにある物体を書いても、前にある物体の手前に書かれることになる。

ちゃんとした表示を行うなら遠近法の変換だったり、前後を比較して書き込む順番を変えたりといった作業が必要になる。そこまでやると3Dのレンダリングエンジンになってしまうし、かなり面倒なことになると思うけども。

それから、この座標変換は1点のみに適用される。なので線や面に対しては適用されないことに注意する必要がある。
例えばGraphicsに四角形を書く場合、通常であればFillRectangleとかを使うことになると思うが、コレでは思い通りの結果にはならないと思う。サンプルの通り、頂点の位置を変換してからFillPolygonで表示すれば予想通りの結果となる。


他の方法としてはGraphicsのTransformを使う方法もあるが、コレは思ったように動かすのはちょっと難しい。ということで僕は早々に見切りをつけてXNAの型を使っている。
ただしXNAを使ったところで線やポリゴンを書くことが限界だから、テクスチャを貼りたいならGraphics.Transformを使う必要があると思う。


ちゃんと3Dを表示させたい場合、最低でも前後の位置関係はどうにかしたい。コレはデータを一旦バッファにためてから、頂点の平均位置を比較して視点から遠い順に書いていく、という感じの処理になるのかな。
とりあえず当初の目標はすでにクリアしているのだけど、もうちょっとレンダリングエンジンについて調べてみると面白いのかも。

2016年8月5日金曜日

SimConnectのUI入力イベント

数年前に開発終了したFSX情報とか誰得だよ、とか思わないでもないですが、おそらくロッキード・マーティンが開発・販売を継続しているP3Dも互換性があると思うので気にせず続けていきます。


さて、SimConnectのUI入力イベントについて。
ここで言うUI入力イベントというのはキーボードの入力やジョイスティックの入力の事を指します。SimConnectでイベントの送信(各種数値の変更等の操作)はすでにできていますから、UI入力イベントをトリガにすることができれば、様々な操作をFSXの設定以上にきめ細やかに作ることが可能となります。

入力イベントを受け取るには、MapClientEventToSimEvent, AddClientEventToNotificationGroup, MapInputEventToClientEvent, SetInputGroupStateが必要になるようです。かなり大変です。

どの順番で操作するのが正しいのかはわかりませんが、P3D Devページのサンプルを元にすると
1) MapClientEventToSimEventでイベントの宣言
2) AddClientEventToNotificationGroupでイベントをグループに関連付け
3) MapInputEventToClientEventでイベントの条件を設定
4) SetInputGroupStateでグループを有効化
という感じのようです。

ソースコードとしては以下のようになります。
simConnect.MapClientEventToSimEvent(EventID.KeyA, null);
simConnect.AddClientEventToNotificationGroup(GroupID.UserInput, EventID.KeyA, false);
simConnect.MapInputEventToClientEvent(GroupID.UserInput, "a", EventID.KeyA, 0, SIMCONNECT_UNUSED, 0, false);

simConnect.MapClientEventToSimEvent(EventID.KeyCtrlA, null);
simConnect.AddClientEventToNotificationGroup(GroupID.UserInput, EventID.KeyCtrlA, false);
simConnect.MapInputEventToClientEvent(GroupID.UserInput, "ctrl+a", EventID.KeyCtrlA, 0, SIMCONNECT_UNUSED, 0, false);

simConnect.MapClientEventToSimEvent(EventID.JoyBtn0, null);
simConnect.AddClientEventToNotificationGroup(GroupID.UserInput, EventID.JoyBtn0, false);
simConnect.MapInputEventToClientEvent(GroupID.UserInput, "joystick:0:button:0", EventID.JoyBtn0, 0, SIMCONNECT_UNUSED, 0, false);

simConnect.SetInputGroupState(GroupID.UserInput, (uint)SIMCONNECT_STATE.ON);

また、クラスの先頭で
enum SIMCONNECT_UNUSED_ { };
readonly Enum SIMCONNECT_UNUSED;
を宣言し、コンストラクタ内で
SIMCONNECT_UNUSED = (SIMCONNECT_UNUSED_)0xFFFF;
のように値を設定しておきます。
SIMCONNECT_UNUSEDはSimConnect内で宣言されていますが、型がuintとなっており、Enum型を受け取るMapInputEventToClientEventには直接渡せないために必要となる措置です。DLLを作った奴が気づかないわけがないので、何らかのうまい対策があるんでしょうが。


上記のコードではAキー、Ctrl-A組み合わせキー、ジョイスティックボタンのイベントを受け取ります。
イベントの発生条件はキーボードとジョイスティックで違い、キーボードの場合はキーを押している間常にキーUpイベント(詳細は後述)が発生します。
ジョイスティックボタンの場合はボタンを押した際にDownイベントが発生し、ボタンを放した際にUpイベントが発生します。
ジョイスティックPoVの場合はボタンを押した際に100倍した度がDataに渡されたイベントが発生します。例えばPoVを右に操作すれば9000、左に操作すれば27000、上に操作すれば0となります。PoVを放した際は4294967295が渡されますが、これはDataがuint型であるためで、int(符号あり32bit)にキャストすれば-1となります。
ジョイスティックAxisの場合は値が変更するたびにイベントが発生し、範囲は符号あり16bitを32bitにキャストしたデータが渡されます。

イベントを受け取るにはOnRecvEventを使いますが、イベントの内容によりSIMCONNECT_RECV_EVENT data.dwDataの内容が変化します。キーボード入力の場合は常に0ですが、ジョイスティックボタンの場合はDownで1、Upで0となり、PoV,Axisは前述のとおりです。


キーボード入力に関しては+でコンビネーションを設定できますが、ジョイスティックの場合は常に1つのボタンしか設定できません。+で組み合わせた場合は一番最初のボタンイベントのみがキャプチャされるようです。
ジョイスティックのボタンを組み合わせたい場合は、自前でBool配列等を用意し、イベントに応じてTrue,Falseを設定、複数のBoolが予め決めた組み合わせになれば何らかの動作をする、という方法になると思います。

ジョイスティックのインデックスは0始まりとなる点に注意して下さい。Windowsのゲームコントローラのプロパティは1始まりなので、プロパティでボタンのインデックスを確認する際には要注意です。



CSVか何かに入力イベントの組み合わせと出力イベントを書いておいて、指定した入力が来れば出力を返すというプログラムを作ったら機体に合わせて設定ファイルを変えたりできて便利かなーと思ってましたが、ボタン組み合わせは結構鬼門ですね。
FSXで設定可能な組み合わせ(キーのコンビネーション or 単一のボタン)だけなら比較的簡単そうですが。
僕が使ってるジョイスティックは親指の届く範囲にボタン5個とPoV、人差し指にトリガ、小指にボタンが1個割り当てられているので、小指でボタンを押しながらPoVを動かせばトリム、とかやりたいので、ジョイスティックの組み合わせが可能ないい方法を考えなくては。。。

2016年8月4日木曜日

FreeTrack

FSXではTrackIRというヘッドトラッカーがよく使われているが、GPLで開発されているFreeTrackというモノもある。FreeTrackは事実上開発終了しているが、ダウンロードは可能だし、Win10でも使用できた。

FreeTrackのインストールや初期設定は難しくなく、結構簡単に行える。
OutputはFSXで使用する場合SimConnect - Flight Simulator Xにチェックを入れるが、FSXを起動していない場合(FreeTrackの設定時など)はSimConnectの接続試行で相当にリソースを食われるので、その場合はSimConnectのチェックを外しておくこと。

以下FreeTrackに書いてある説明。目で読んで手で入力してるので間違ってたらごめんね。

*1Single Point
Two degrees of freedom, hands-free mouselook.
Easy to setup.
Potentially higher precision than tracking methods.
Head translation treated as retention.

*3Points Clip
Six degrees of freedom.
Headphone mounted mode.
Webcam aligned with model.
Middle point translation reference.
Y translation and Roll may be limited if the model is large.

*3 Points Cap
Six degrees of freedom.
Cap mounted model.
Top point translation reference.
Three LEDs easier to power than four.
Must ensure model plane does not parallel camera plane.
Less chance of points intersecting than four point.
X translation may be limited if the model is large.

*4 Points Cap
Six degrees of freedom.
Cap mounted model.
Mostly for back compatibility.
Top point translation reference.
Four LEDs harder to power.
Yawing limited by distance between points and X translation may be limited if the model is large.

LED配置モデルは4種類あるが、4個目は互換性問題によって残っているとのこと。

1Single Pointの場合は、セットアップ(ハードウェアを含め)が簡単で、他の方法よりも(視線の上下左右に限れば)高精度なトラッキングが可能、とのこと。ハンズフリーマウスルックと書いてある通り、スペースキーを押しながらマウスで視点を変更するのと同じ視点操作をハンズフリーで行うことができる。
このモデルは動作確認が非常に簡単で、カメラの輝度や閾値を設定すれば他にはゲインをいじる程度で終わる。追尾対象は自分で用意する必要があるが、ヘッドランプのナイトモードを使うとちょうどいい輝度だった。なのでナイトモード搭載のヘッドランプとWebカメラをすでに持っている人は追加リソース無しで簡単に動作確認ができる。
ただし2軸しか制御出来ないため、計器パネルを覗き込むといった動作は不可能。あくまでPoVハットの代わりとしてしか使えない。なのでLearjetみたいな後方視界の悪い機体には向かない。

他のモデルは6自由度あり、視線の向きと原点を移動することができる。これらのモデルであれば計器盤を覗きこんだり、後方視界の悪い機体でも窓に顔を近づけて斜め後ろを確認したりということが行えるようになる。ただ原点移動はかなりクセのある動きなので、慣れるまでが大変。

僕は1SinglePointと3PointsClipを試してみた。どちらかと言えば3PClipの方がいいが、安定性がかなり悪いので、そういう意味では1SPの方がいい。でもこれならジョイスティックのPoVで操作できるので要らないかも。


僕の予想では4Points Capは適切に処理されていればロバスト性が高くていいと思うのだが、なぜか「互換性のために搭載している」というような後ろ向きな説明がなされている。


手っ取り早くヘッドトラッカーを使いたいならTrackIRを買うのが一番だと思う。いろいろ試行錯誤するよりも2万円のほうが安いと思う。
ただ自分の時間単価がゼロに近いとか、どうしても自分で作らなければいけない理由とかが有るならFreeTrackを使うのもありだと思う。
FreeTrackもすでに開発が停止しているので、ソフト側も自分で作ったほうが良いかもしれない。FSXやP3Dで使うだけならSimConnectで渡すだけなので転送プロトコルとか気にしなくていいし。FreeTrackは痒いところに手が届かない感じがしているので、いずれはその方向に行くかも。
だけどしばらくはFreeTrackで頑張ってみようと思う。

2016年8月3日水曜日

C#のフォームをアクティブにさせない

基本的にOSのGUIというものはどこかをクリックすればそのウインドウ(あるいはデスクトップ)がアクティブとなり、以降のキー入力等はそのアクティブなプログラムに渡されることになる。
が、これでは都合がわるい時がある。プログラミングで何かを作っている時に一番有り得そうなのは「とりあえずスクリーンキーボードみたいなの作ってみるか~」って時だと思う。スクリーンキーボード自体がアクティブになってしまっては入力したキーがすべて自分に帰ってきてしまう。
そういう時はC#のFormをアクティブにしないような工夫が必要となる。

private const int WS_EX_NOACTIVATE = 0x8000000;
protected override CreateParams CreateParams
{
    get
    {
        CreateParams p = base.CreateParams;

        if (!DesignMode)
        {
            p.ExStyle |= (WS_EX_NOACTIVATE);
        }

        return (p);
    }
}

とりあえずは上のコードをFormの適当な場所に貼り付けておけばよろしい。

このようにした場合、Formをクリックしてもアクティブなウインドウは変化しなくなる。
ただしタイトルバーのクリック等はそのまま有効になるから、タイトルバーをドラッグして移動とかは可能。タスクバーのアイコンをクリックした場合はFormはアクティブになるから、その場合はキー入力がこのFormに渡されることになる。

いくつか欠点があるとすれば、FormのTextBox等の入力が不可能、Formを移動した場合に別のウインドウの裏に潜り込む場合がる、等。後者についてはタスクバーで一旦このプログラムをアクティブにしてやるか、プログラムを常に手前に表示するようにする必要がある。現実的には常に手前に表示が楽だと思う。


FSXは非アクティブになってもシミュレーションを続行することは可能だが、音が出なくなってしまうため、SimConnectとかでボタン操作とかを渡すプログラムはこういう工夫が必要になる。
FSXでフラップとかギアの操作にキーボードを使うのもなんかマヌケな感じがするので、Formにボタンとか配置してAPのモード切り替えできたら楽しいかなとか思ってる。もっとも、僕のPCは非タッチ液晶がプライマリとして正面にあり、タッチ液晶のセカンダリが右側にあるので、右手でスティックを持っているとタッチ液晶に手が届かない。「機長席に座ってる気分で左手でスティックを操作すればいいだろ」と言われれば全くそのとおりなのだけど、右利きの人間としては左手で飛行機を操縦するのは正気の沙汰とは思えない。とはいってもボーイングもエアバスも関係なく機長は左手で操作するわけだから、慣れとかなんだろうが。