Archives

January 2009 の記事

メニューから任意の画像を読み込めるように変更しました。(前回)
対応フォーマットも大幅に増えて、任意サイズの画像を直接背景として登録できます。

d3dmclock HTC Touch Diamond
↑ Windows7 ぽい感じに・・

ダウンロードはこちら
d3dmclock v1.10 ダウンロードページ

WindowsMobile 用アプリです。

速度はだいたい 16~21fps 程度です。
速度は主に時間帯と背景テクスチャの解像度で変わります。
時間帯で速度が変化するのは、各数字モデルのポリゴン数が違うから。

1024x1024 に変換されてしまう大きな画像だともっと速度が落ちるかもしれません。
HTC Touch Diamond は VGA (640x480) なので、512x512 がちょうど良いくらいでしょう。
QVGA の端末ならもっと小さくて構いません。


Touch Diamond は 2.8インチで VGA (640x480) の解像度があるため、かなり密度が
高くきれいに見えます。輪郭のジャギも気にならないし、この密度でリアルタイムに
レンダリング出来てリアルタイムに動いているのだから十分かもしれません。
blog のキャプチャは 1/4 に縮小しています。

WindowsMobile だと、せっかく載っている 3Dアクセラレータがあんまり活用されていないのは
もったいないですね。


テクスチャの読み込みは、当初 Direct3DMobile D3DMX のテクスチャローダーを
そのまま使用していました。これ対応フォーマットは dds と bmp だけ。
しかも bmp は 16bpp を読み込めないことがわかりました。
さらに 2の n乗以外のサイズも読み込めるけど固まったかと思うくらい低速です。

テクスチャローダーを作り直しました。
参考にしたのは WindowsMobile6.0 SDK サンプル
Windows Mobile 6 SDK\Samples\Common\CPP\Win32\Imaging

これで bmp だけでなく jpeg や png 、gif などメジャーなものはたいてい
読み込めるようになりました。しかも速いです。

流れは次の通り

(1) IImage として画像を読み込む
(2) IBitmapImage に変換する
(3) LockBits() してピクセル情報にアクセス
(4) SystemMemory の Surface (CreateImageSurface()) に格納する
(5) CreateTexture()
(6) Texture から Surface を取り出す。
(7) (6) の Surface に (4) の Surface をコピーして vram に転送
(8) テンポラリリソースとインターフェースを Release

#include  <imaging.h>

// interface 取得
IImagingFactory*	iImageFactory= NULL;
CoCreateInstance( CLSID_ImagingFactory, NULL, CLSCTX_INPROC_SERVER,
			IID_IImagingFactory, &iImageFactory );
// ファイル読み込み
IImage*	iImage= NULL;
iImageFactory->CreateImageFromFile( file, &iImage );

ImageInfo	info;
iImage->GetImageInfo( &info );
int	w= info.Width;
int	h= info.Height;

// bitmap に変換
IBitmapImage*	iBitmap= NULL;
iImageFactory->CreateBitmapFromImage( iImage, w, h,
	PixelFormat24bppRGB, InterpolationHintDefault, &iBitmap );

iImage->Release();
iImageFactory->Release();

// lock
static BitmapData	lockdata;
iBitmap->LockBits( NULL, ImageLockModeRead, PixelFormat24bppRGB, &lockdata );

lockdata.Width;
lockdata.Height;
lockdata.Stride;
lockdata.Scan0;

// texture を作成
IDirect3DMobileTexture*	iTexture= NULL;
CreateTexture( w, h, 1, 0, D3DMFMT_R5G6B5, GetTexturePool(), &iTexture );

// SystemMemory の surface を作成
IDirect3DMobileSurface*	iSurface= NULL;
CreateImageSurface( w, h, D3DMFMT_R5G6B5, &iSurface );

// 書き込み
D3DMLOCKED_RECT	dlock;
iSurface->LockRect( &dlock, NULL, 0 );
uintptr_t	di= reinterpret_cast<uintptr_t>( dlock.pBits );
for( int y= 0 ; y< h ; y++ ){
    unsigned short*	dp= reinterpret_cast<unsigned short*>( di );
    for( int x= 0 ; x< w ; x++ ){
        const unsigned char*	sp= image.GetXY( x, y );
        unsigned int	b= sp[0];
        unsigned int	g= sp[1];
        unsigned int	r= sp[2];
        *dp++= ((r<<8)&0xf800)|((g<<3)&0x07e0)|((b>>3)&0x001f);
    }
    di+= dlock.Pitch;
}
iSurface->UnlockRect();

// texture の surface を取り出す
IDirect3DMobileSurface*	iTexSurface= NULL;
iTexture->GetSurfaceLevel( 0, &iTexSurface );

// コピー
CopyRects( iSurface, iTexSurface );

iSurface->Release();
iTexSurface->Release();

// unlock
iBitmap->UnlockBits( &lockdata );
iBitmap->Release();



関連エントリ
HTC Touch Diamond で Direct3DMobile その(9) d3dmclock v1.00 3D クロック
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



せっかくなのでもうちょっと実用的なやつを。
デジタル時計にしてみました。

d3dmclock

HTC Touch Diamond の加速センサーに対応したので、本体の傾きに合わせて画面が
スムーズに回転します。
意味もなく数字 1つ 1つがポリゴンです。

d3dmclock
↑数字が不揃いなのはバグではありません。

ダウンロードはこちら
d3dmclock v1.00

WindowsMobile6.0/6.1 で動作します。加速センサーが無くても起動します。
ただし 3Dアクセラレータがないと非常に低速です。
おそらく HTC Touch Diamond 系、Touch Pro 等で動作すると思われます。

加速センサー以外にも、画面タッチの左右で回転、上下でズームします。

d3dmclock

↑どんな角度にもなります。

背景画像は適当なので入れ替えて使ってください。
プログラムと同じフォルダに入っている bgimage0.bmp ~ bgimage2.bmp です。
サイズが 256x256 や 512x512 ならおそらく何でも大丈夫です。


textimage0.dds~textimage7.dds ファイルを置き換えれば、数字用のテクスチャも
入れ替えできます。こちらもサイズは 2の n乗で mipmap が必要です。
フォーマットは R5G6B5 にしてください。


Menu の Light にチェックを入れると、光源を操作できます。
この状態で画面をタッチすると、タッチした位置から画面中央に向かうベクトルを
光源の向きと見なします。

iPhone/iPod touch みたいにスムーズに動くものを、と思ったけどなかなか難しいですね。
Touch Diamond 系をお持ちの方はよろしければ一度お試しください。


関連エントリ
HTC Touch Diamond で Direct3DMobile その(8) ノーマルマップの解説
HTC Touch Diamond で Direct3DMobile その(7) ノーマルマップを表示する
HTC Touch Diamond で Direct3DMobile その(6) 頂点性能続き
HTC Touch Diamond で Direct3DMobile その(5)
HTC Touch Diamond で Direct3DMobile その(4) 高速化
HTC Touch Diamond で Direct3DMobile その(3) 実際の頂点性能
HTC Touch Diamond で Direct3DMobile を使う。その (2)
HTC Touch Diamond で Direct3DMobile を使う。ハードウエアアクセラレータ



Touch Diamond には、触れただけで反応するタッチセンサーが付いています。
ボタンやカーソルキー周りがそうです。

このタッチセンサーの値を受け取るには HTCAPI.dll を呼び出します。
ウィンドウに関連付けされ、あとはモードを設定するだけでメッセージ (WM_USER~)
で通知が来るようです。
参考にさせていただいたのは加速度センサーの読み出し同様こちらです。

Scott's Weblog Fun with the Diamond's tilt sensor

タッチセンサーは左右に分かれており、それぞれ個別に座標値が取れます。

・中央のカーソルキーから左側、通話ボタンやホームボタン側
・中央のカーソルキーから右側、終了ボタンや ← (OK) ボタン側

座標値は左上が原点 (0,0) で、0~67 くらいの範囲。
ただし中央のカーソルキー周囲は回転のジェスチャーとして識別されるため座標が
取れません。

中央の決定ボタンは、座標が不正な範囲 (255,255) の状態で押されることで
判別できます。カメラのオートフォーカスで半押し状態がこれ。

タッチしたイベント (DOWN) と指を離したイベント (UP) が送られてくるだけで、
MOVE イベントがありません。押した座標、離した座標は取れますが、移動中の
イベントがないのでスライドによるスクロールなどには向いていないようです。

液晶面の感圧式タッチパネルだとスクロール等のスライドを指で行うのが
難しかったりするので、本当ならこちらのタッチセンサーでスクロールできたら
よかったのですが。


また左右のパネル両方同じイベント扱いなので、どちらかにタッチしてしまうと
DOWN 状態になります。座標は独立して取れるものの状態が共有なので、
マルチタッチのような使い方にも向いていないようです。

なお液晶面のタッチパネルとは別物なので、タッチボタンと液晶画面など別センサー
同士の同時タッチは可能です。


HTCNavClose() の引数は DWORD ではなく、HWND が正しいようです。
よって HTCNavSetMode() で与えた DWORD を Close() するのではなく、使い方としては
HTCNavOpen() に与えた HWND を指定して一度だけ Close() します。

中央の回転ジェスチャーは WM_USER + 200 だけでなく WM_USER + 201 にも何らかの
データが送られています。終了イベントのようですが、送られないこともあります。

いろいろ試したけど生の値が取れる方法はまだわかりません。
とりあえずボタン部分に触れただけで反応するような感度の良いアプリは作れると
思います。

同じ部分に指を置いても

(1) 触れる → タッチセンサー
(2) 押し込み → ボタン
(3) 触れたまま揺らす → 加速度センサー

といった使い分けもできるでしょう。


関連エントリ
HTC Touch Diamond のボタン部分とタッチセンサー
HTC Touch Diamond の加速センサー



HTC Touch Diamond の加速センサーから値を読み出してみました。
参考にさせていただいたのは下記サイトのプログラムです。

Scott's Weblog Fun with the Diamond's tilt sensor

API は Touch Diamond に含まれている HTCSensorSDK.dll を呼び出しています。
取り出せる値は 3軸センサーのそれぞれの加速度 X Y Z。
ソースにはだいたい -1000~1000 程度の範囲で傾きと書かれていますが、
やはり重力加速度を表しているようです。
何もしない状態で ±1000 前後 (1G)。強く振ると ±2400 程度の値も取れます。

データの連続取得は 40msec 程度の間隔が必要です。
当初動かしたときは 1/30 や 1/60 sec でサンプリングしていたため、値が更新されず
少々悩みました。

使用しているセンサーはレジストリの DeviceVendor より Kionix KXSD9

direction=5  液晶背面方向 -Z
direction=4  液晶面方向   +Z
direction=1  左サイド     -X
direction=0  右サイド     +X
direction=2  下           +Y
direction=3  上           -Y

struct SENSORDATA {
    short   ax;
    short   ay;
    short   az;
    short   zero;
    int     AngleY;
    int     AngleX;
    int	    direction;
};


例えば机の上に水平に寝かせた状態だと、液晶背面方向が重力方向だから
az = -1000 前後に、direction が 5 になります。
SENSORDATA の最後の値はこのように、大まかな端末の向きをそのまま表して いるようです。
いるようです。
AngleX/AngleY はおそらく極座標か何かだと思いますが未調査。


関連エントリ
HTC Touch Diamond のボタン部分とタッチセンサー



HTC Touch Diamond の画面の下、ボタンのあたりは一枚板になっています。
だけどボタン部分は押し込むことができて、ちょうど iPod のクリック&ホイール
みたいにカチッと反応するボタンが埋まっています。

この部分、アプリによっては押し込まなくても操作可能です。
カメラだとオートフォーカスの半押しができるし、Opera でカーソルのあたりを
丸くなぞるとズームできるし、その仕組みが不思議でした。
どうやらボタンの上、板の部分全体がタッチセンサーになっているようです。

液晶画面の方はこれまでのタッチパネルと同じ感圧式で、スタイラスが使えるし
ある程度圧力をかけないと反応しません。

ボタン側の方は iPhone/iPod touch のように指で触れるだけで反応するし、
スタイラスに反応しないため静電容量式だと思われます。

クリックボタンが押しにくくなるだけなのに、わざわざ一枚板になっている理由も
これでわかります。

PS3 のワイヤレスキーパッドみたいに、気がつかないところで使われている
タッチセンサーはこれからも増えそうですね。

タッチの情報を取得すれば、ズーム操作と重なるかもしれないけど
触れるだけで反応できるカーソルキーとか、
触れた場合と押し込んだ場合で動作が変わるボタンとか作れるかもしれません。
使いやすいかどうかは別として。


Touch Diamond には加速センサーが入っています。
3軸のセンサーのようで、Wii Remote 同様に重力加速度で傾きがわかります。
この情報にアクセスする方法を解析された方はいらっしゃるようで、
サンプルプログラムなどもおいてありました。

Scott's Weblog

タッチセンサー情報が見れたのもこのプログラムのおかげです。