気まぐれな戯れ言 バックナンバー1


気まぐれな戯れ言バックナンバーです。全体の一覧はこちら

99/09/14 2つの返り値編
99/09/09 ファイルの日付編
99/08/20 midiStream編
99/08/11 D3DRM(再)編
99/08/09 D3DIM+RM編
99/08/03 キー名取得(+HP更新ネタ)
99/07/30 αブレンド−DrawPrimitive編
99/07/27 MIDファイル解析編
99/07/20 ブレンド編の補記
99/07/16 テーブル利用αブレンド編
99/07/13 アセンブラ挑戦中級者編
99/07/11 アセンブラ挑戦初心者編

99/09/14 2つの返り値編[★★◎]
SoundToolsのSTGetBufferSizeが正常に働いていないというバグ報告を受け、早速修正に乗り出す。
どうもv1.0からv1.3への移行時に元ソースを乗り換えたのが原因らしい。
以前ELライブラリだっものをDirectX SDKにあるDonutsのソースにしたんだけどさ。
あれは重ね合わせ数を自由に設定できるようにSndObjなる構造体+関数(クラスにはしてないみたいだけど)を使ってるんだよね。

んで、SndObjを作る関数内では・・・
1つ目のIDirectSoundBufferを作り、そのポインタを保持。
上のバッファのポインタからDuplicateSoundBufferで複製。

んで、サウンドのバッファサイズを取得するにはどうしたらいいか。
一番ラクなのは1つ目のIDirectSoundBuffer作成時にバッファサイズをDSBUFFERDESC構造体のdwBufferBytesメンバに入れるからそのデータを保持する。

ただ、ここで問題なのが・・・1つ目のIDirectSoundBufferの作成を別関数で行ってるんだけど、その返り値がIDirectSoundBufferのポインタなのね。
つまりサイズを返すことができないと。

一般に複数の返り値を得たい場合は次のようにする。
1−返り値を構造体にする。
2−引数として変数のポインタを送る。
3−上位ワード・下位ワードと分ける。

1は普通嫌われるんだけど・・・。
なぜなら、返り値はいったん複製されて送られるんだけど、でかいサイズはやっかいと。
10000byteとかある文字列なんかも同様に嫌われるのね。
普通この場合はポインタを利用するんだけど。
VBでは結構強引にやっちゃうんだけど・・・内部ではポインタで処理してるかもね。

2とかでもいいんだけど・・・
元関数、とくに引数リストをいじるってのがチョット気が進まない。
出きる限りDirectX SDKのコード部分は壊したくないので。
それにヘッダーファイルのほうのプロトタイプも修正するのが面倒だし(すぐ済むけど)

3は一部のAPIで行われてるちょっと強引な方法。
ただ、サウンドバッファのサイズなんてのはWAVファイルのサイズより少し小さいぐらいの値を取るから、十分ワードサイズの領域には収まらないわけね。
十分にダブルワード分の領域を利用するべき。

ってなわけで元関数をいじるのは止めることにした。
強引な方法として1つ目に作成したIDirectSoundBufferを利用してGetCapsからサイズを得る方法。
まぁこれで問題ないだろうけどね。
そうそう実行速度に影響あるモノでもないだろうし。

ってなわけで久々のSToolsのDirectSound関連の機能の修正ね。
バグ連絡をくれた清水さん、ありがとうございます。

99/09/09 ファイルの日付編[★★★◎◎]
テストも(いろんな意味で)終了し、秋休みに入った。
なんかソフトやデータをDLしていてDLした日付をチェックするため、日付を一括してそろえるソフトを作ろうかななどと考えた。
だってLZHファイルはDLした日付が設定されても解凍したファイルはそうじゃないので。

いままでの拡張子変更ソフトとかみたくVBでNameステートメントとかあるかなぁとか思ったんだけど・・・甘かったみたいね。
どうやらSetFileDateなるAPIを使う必要があるみたい。
んで、SetFileDateの引数をみると・・・日付指定がFILETIME構造体とかいう要は64bitの数値なんだよねぇ・・・
しかもそれが100ns(msじゃなく)単位の数値らしい(^^;)

んで、わざわざ計算しないといけないのかとか思ってたらSystemTimeToFileTimeなるAPIがあるみたい。
ただ・・・またSystemTimeToFileTimeに必要なのがSYSTEMTIME構造体。
これがまたメンドウ。
年・月・日・時・分・秒・ミリ秒をそれぞれのメンバに入れる必要があるみたい。

当初VBのDate型変数を使って「99/09/09 12:00:00」みたく表記したものを利用するつもりだっただけにメンドウ。
まぁこれはDate型からYear関数とかMonth関数とかで取得すればいいかと。

んで、なんとかDate型→FILETIME型にはできるとして・・・
再び問題なのがファイルの指定。
これがファイル名で指定できればいいんだけど・・・
ファイルのハンドルを送らないといけないんだよね・・・
これにはCreateFileを使わないといけないんだけど・・・
なんかセキュリティ設定とか面倒だなぁ・・・

なんかメンドウだからまた今度にしよう(^^;)

99/08/20 midiStream編[★★★◎◎◎]
注:あらかじめ言っておきますが、最終的にmidiStreamでのMID演奏は実力不足のためにうまく行きませんでした(^^;)

これは今月上旬に試してたことなんだけどせっかくなので。

最近「VBでリソースからMIDを再生したい」という質問を受けたんだよね。
んで、返した返答はこれ。

1−リソースをテンポラリファイルとして展開し、通常のmciSendStringやmciSendCommandで演奏する。
2−midiOut系APIで処理する。(これはSoundToolsとおなじだけど・・・)
3−midiStream系APIで処理する。
4−DirectMusic?(現段階では結局VBから直接使えないので今回は却下)

んで、それぞれの短所長所について。

1−<長所>
比較的手間がかからない。
危険な処理(コールバック)を利用する必要性がない。
<短所>
(まずありえないが)既に同名の別のテンポラリファイルなどがあったりしたらどうするのか。 (ただ、これに関してはGetTempPathとかGetTempFileNameでまず重ならないようにできる)
エラーなどで終了したりするとテンポラリファイルが残ってしまう。かといってmciSendString直後にファイルを消し、また再生直前にファイル作成などをすると頻繁にファイルアクセスが起きる。

2−<長所>
自前の処理などでキーシフトでもテンポ変更でも自由自在。
<短所>
マルチメディアタイマーを使うため、デバッグが困難。
また、VB環境下だとMID再生だけで処理が重くなる。
MIDファイルの構造を知らないと利用できない。
(SoundToolsはこれをCでDLLを組むことで回避してますが・・・)

3−<長所>
2のマルチメディアタイマーの利用よりもテンポ取りが確実。
ループなども1よりは確実。
<短所>
(MIDからの演奏の場合)midiStreamの形式にコンバートしないといけない。
とくに、トラック数が1の場合はいいが多くなれば多くなるほどコンバートがメンドウ。
(MDSからの演奏の場合)圧縮形式からの展開がメンドウ。ファイルサイズが大きくなる。
一度に64KBまでしかバッファを送ることができないため、少し大きなデータを送る場合はメンドウ。
結局コールバックを使うはめになる。

まぁほんとは2でSoundToolsの利用を勧めたいところだけど(をい)まぁ今回の主題でもある3を試してみることにする。


ひとまず自分が持ってたmidiStreamに関するサンプル。
いずれもC++で書かれてるけど・・・

−VC付属のもの−
(MIDからの再生)マルチメディアシステムサンプルとしてのMIDプレイヤー
(MIDからの再生)DirectXサンプルとしてのMIDプレーヤー
(MDSからの再生)DirectX3サンプルとしてのゲームでの再生ルーチン

−その他−
(MDSからの再生)オンラインでみつけたmidiStream再生クラス
(フォーマット0のMIDからの再生のみ)質問してくれた人が送ってくれたどこかの(?)サンプル

まずMDSはファイル形式がよくわからない上にファイル展開のやりかたもよくわからないので却下。
また、VC付属のものは一体何やってるかわからないので却下。
しかもVCのものはUIの処理とかがあってコードの進み方がわからん・・・
やっぱり他人のコードってわかりにくいよな。

一番シンプルなもらったサンプルで試す。
これはほとんどUIが無いため、MIDのデータコンバートに集中できるんで。

んで、まずmidiStreamの演奏できる形式をチェック。
どうやらMIDIEVENT構造体の連発らしい。
これはDeltaTime、Eventが入っているだけ(実際はReservedなメンバもあるが・・・)なのでシンプル。
&HFF系のメタイベントはテンポ以外はすべてカットできるので、唯一メンドウなのはシステムエクスクルーシブ。

ただ、、、問題なのが、ここではトラックを分けたりできないと言うこと。
だから上のサンプルではトラックが1のときしか演奏が不可能みたい。

んで、トラックを1つにしつつmidiStream形式にすることを考える。
また、このときに一体どれだけのMIDIEVENTが必要になるかわからない上にSysExが出てくるとMIDIEVENT構造体の中身が12バイトになるとは限らないため、MIDIEVENT構造体でなくLong型の配列を利用することにする(Cなどでポインタの知識があればここらへんの処理には困らないし・・・)
MIDIEVENTではSysExはダブルワード境界にあわせないと行けないので、逆にLongに統一できると。

んでコンバートのコード作成。
まず一気に全データをチェックしてMIDEVENTの数をチェックする。
実際には通常のイベントではLong3つ、SysExではSysExの長さをLバイトとすれば(3+(L+3)¥4)つでOK。

ここでトラックの統合処理を行う。
次の順でいいかと。

1−すべてのトラックの現在位置でのTick数をチェック。
2−一番小さいTick数のトラックのデータをMIDEVENTにいれる。
3−このトラックのデータをまた1イベント分読み出し、再び現在位置のTick数を計算して1にもどる。

ひとまず試したところ、まぁだいたいうまくいってるかと(予想)
ファイルの長さが約3倍程度になるのも予想通りだし・・・

んで、実際の演奏処理だけど・・・基本的には、

これはmidiStreamから「バッファの中身の演奏が終了したよん」メッセージを受け取ったら次のデータを送る。

につきます。
だから、このときに同じデータを何度も送ればループとかもできるわけだし。
また、ここでコールバックを利用するんだよね。

上にも書いたとおり、一度に64KBまでしか送れないので分割する処理を組んだんだけど・・・
なぜかうまく行かない。

一応1度目のコールバックはちゃんと来てるんだけど・・・
なんで演奏されないんだろ?

99/08/11 D3DRM(再)編[★★★◎◎◎]
とにかくテクスチャーだけ貼りたいなぁと言うことで再挑戦。
ひとまず前回のサンプルでのXファイルを分析してみることにする。
サンプル付属のXファイルはバイナリ形式なのでCONVX.EXEでテキストにすると・・・

TextureCoordinateが全部(0,0)になってるじゃん。
こりゃダメなのも当然だな、と。

では他のサンプルはどうなんだろうということでDirect3zのサンプルを見ると・・・
これはXファイル内にTextureCoordinateがちゃんと指定されてるからダメだなぁと。
(いや、TextureCoordinateが指定されてない状況からでもテクスチャー貼りたいじゃん)

んで、今度はELライブラリ付属のサンプルを見る。
ん?TextureCoordinateが(0,0)なのにテクスチャーが貼られてるぞ?
そのXファイルを元のサンプルに持ってくると・・・失敗(^^;)

まぁこれでELライブラリはコードで内部からテクスチャー設定をしていることが判明。
んでコードの解析を試みる。

うーむ。
どうもIDirect3DRMWrapでやってるみたいだね。
ラップ作成の軸指定がぜんぜんわからないのでELから抜き出して利用(ELは改変可能だし)。
おお!うまくできた。

しかし最初から3Dソフト側でTextureCoordinateを書き出してもらった方がよっぽどラクだな(^^;)
ちなみにELでもフレームはオブジェクトごとに作ってたね。

しかしMeshBuilderからMeshを作ってSetGroupTextureでもいいのかな?
よくわかんないけど。

DrawTools風にD3DToolsとか作ってみるかなぁ・・・
D3DIMなんてイヤだけど。

ちなみに。
下の日記のサンプルではMeshBuilderやマテリアル・子フレームを即Releaseしてるけど、Bio_100%のBBSで質問したところ、これらは参照カウンタがAddVisual時にインクリメントされてるのではないかということっぽいです。
つまり作成時にカウンタが1になり、AddVisualで2になり、作成直後のReleaseでまたデクリメントされて1になり、最後に親フレームのRelease時に子フレームなどがもう一度Releaseされて参照カウンタが0になり、本当にReleaseされると。

しかしD3DRMでテクスチャーが貼れてもD3DIMには所詮通用しないんですな(^^;)
D3DRMはD3DIMの多機能版というだけあってテクスチャーを貼るのもロードするのもD3DIMよりラクだし。
テクスチャーをわざわざいったんサーフェスに準備しないといけないし。
そう考えるとD3DRMのテクスチャーはD3Dの中では異例のやりやすさかも。
なんてったってIDirect3DRM->CreateTextureとかIDirect3DRM->LoadTexture系の命令でメモリ・ファイル・リソース・サーフェスのどこからでも一発でテクスチャー作成できるし。

しかしD3DRMでも半透明テクスチャー貼れるのかな?
少なくともカラーキー設定はできそうな感じだったけど。

99/08/09 D3DIM+RM編[★★★★◎◎◎]
ああ、未だに半透明がうまく行かない・・・

ひとまずもう一度D3DIMで半透明に挑戦!
今回はPPlaneでなくほかのサイトで見つけたDrawPrimitiveサンプルで挑戦。
ただ、なぜかサンプルのEXEですら起動しなかったため、なぜかと思ってデバッグしたらテクスチャーのあるサーフェスのGetDCがうまくいってなかったみたい。
まぁよくわからないんでテクスチャーをひとまず外す。

以前四角形描画がうまく行かないのは2つ目の三角形を左回りで頂点の指定をしてたからみたいだったんでまずそれを修正してみる。
これのせいでD3DPT_TRIANGLESTRIPだとダメなんだけど・・・
長方形をどうしても展開形の三角形2つに分割しようとすると片方は左回りになっちゃうんだよねぇ・・・
D3DPT_TRIANGLEFANなら長方形ABCD(Aから右回りでB、C,Dとする)でABC、ACDの順で描画すればOKだったかも(^^;)

まぁそれはいいとしてまずテクスチャー貼りと。
ん?
簡易化のためにライトとマテリアルを全部削除してるのに・・・なんでテクスチャー無しなのに色がついてるの?
しかも白とか灰色とか黒ならともかく青っぽいし・・・

んで、テクスチャーに挑戦。
相変わらずGetDCでうまくいってない。
なんで単なるサーフェスがGetDCに失敗するのかよくわからないけど・・・
結局半透明はまたあきらめ(^^;)


ついでなんでD3DRMに挑戦。
RMは個々のポリゴンをいじったりするのには不向きだけど、出来上がってる物体の位置をいじったりするだけならこれで十分。
別サイトで落とした簡単なサンプルを利用。
(今考えればDirectX5でよければ、かのDirect3Zで初期化はラクだな)
よくわからないのがフレームの考え方。
同じオブジェクトをたくさん並べたいときはいったんIDirect3DRMMeshBuilderをロードしてあとはたくさんフレームを作って貼りつければいいのか?
ってなわけでひとまずフレームを2つにしたらやはり表示される物体は2個になったけど・・・
でもそのサンプルだとフレーム作って回転とかを設定した後AddVisualでMeshBuilderを設定したらその後すぐに子フレーム・MeshBuilderともにReleaseしてるんだよねぇ・・・
それでも親フレームには設定が残ってるのかな?
フレームはともかくMeshBuilderのポリゴンデータまで残すのかなぁ・・・

まぁそれはともかくとして再びRMでテクスチャー利用に挑戦。
D3DIMのIDirect3DTexture2だとどうもテクスチャー用のサーフェスを用意してからGetHandleしないといけないっぽいんだけど、D3DRMのIDirect3DRMTextureだとIDirect3DRM::CreateTextureとかLoadTextureでいきなりファイルからテクスチャーを作成できるんだよねん。

んで早速テスト・・・
ん?
なぜかオブジェクトの色は変わったんだけど・・・テクスチャーが正確に貼れてないぞ。
いくつかのテクスチャーで試したらどうも(0,0)の色だけが全体に貼られているっぽい。
うーむ。
SetTextureだけだとダメなのかな?
わざわざUベクトルVベクトルも設定しないといけないとか。
単なる円柱や球ならともかく一般のポリゴンなオブジェクトのテクスチャーのテクスチャーのUV座標なんぞ自分の数学力では指定できないぞ。

もしかしてテクスチャーはSetTextureしてAddVisualしたからってReleaseしちゃいけないのかな?

しかしDirectX5で導入されたDrawPrimitiveを利用するにはDirectX6 SDKを持ってない自分はDirectX5 SDKの英語ヘルプを見ないといけないんだけど・・・普通のD3DRMだとDirectX5付属のDirectX3 SDK日本語版ヘルプを使えるからラクだよねぇ。
っていうか何でバージョンが1つ落ちないと日本語ヘルプが出ないんだろ。
DirectX6 SDKもMicroSoftからはダウンロードできないし・・・
あの分厚いSDK本を買わせようってか?
DirectXから2・3・5・6と全部出てるよな(^^;)
しかも6にもなると\10000だし。

しかしD3DRMもなんかわかりにくいと思ったらIDirect3DRMVisualってIDirect3DMeshBuilderの下位にあるヤツなんだね。
IDirect3DRMObjectみたく。
今日気づいたよ。
ただ未だにIDirect3DRMUserVisualってのが不明。
これを使うとIDirect3DDevice2が使えるって話だけど。

99/08/03 キー名取得(+HP更新ネタ)編[★★★◎◎]
さて、1999/07も通過しました。
これでもう「7の月」などと言う言いかたは一気に消えることでしょう。
不安を増やしてうれしいか?>研究してる人とかは困ったろうね>TV○ックルとか。
ノストラ時代の暦ってなんだろ?
今はグレゴリ暦だっけ?

最近そこそこハードな内容(?)に突っ込んでたのでもうちょいソフトなプログラムでも組んでみようかと。
んで、以前からJoyBoardに組みこんでみたかったキーコード→キー名なプログラムに挑戦。
JoyBoardではキーコードそのままで処理するから評判悪かったんだよね(^^;)
キー名を得るには走査コードを得る必要があり、VBからコレを得るには次の2つの方法がある。

1−仮想キーコード→走査コード
これは通常のKeyDownイベントなどで手に入るKeyCodeをMapVirtualKeyで走査コードに変換し、それをGetKeyNameTextに送る。

2−メッセージフック
本来WinではKeyDownのイベント(WM_KEYDOWNメッセージ)の出力時は同時に走査コードを送ってくるため、それをGetKeyNameTextに送る。

オマケ−キーテーブル
あらかじめキー名の一覧を作っておき、それを参照する。
DOS/Vと98などではキー名が違うこともあるため、INIファイルなどにして自由に書き換えられるようにしておく。
これは単純だけどユーザーに手間をかけさせることになるんで最終手段だね(^^;)

ってなわけで、まずはラクそうな1に挑戦。
失敗(^^;)
GetKeyNameTextは走査コードのほかに拡張キー名を返すかどうかと、左右判別をするかどうかというフラグを送るんだけど・・・
この拡張キー名がクセモノ。
通常のキーはいいんだけど特殊なキーとかが問題。
たとえば拡張キー名を返さないにするとカーソルの上を押してもなぜか「NUM 8」が返ってくる。
逆に拡張キー名ありにするとテンキーの8を押しても「UP」が返ってくる。
仮想キーコードから拡張キーかどうかを判別する機能は無いのか?
っていうかMapVirtualKeyはそこまでしてくれないの?

と言うわけで逆で攻めることに挑戦。
走査コードもせいぜい256個なので、拡張キー名を返す・返さないをあわせても512個。
これらのキー名を列挙しておいて、走査コード→仮想キーコードを行っておき、あとはこれを参照する。
かと思いきや、逆もやっぱり拡張キー名は対応していないみたい。

ってなわけでしょうがなくコールバックを使ってメッセージフック。
以前作った「ファイルドロップサンプル」のHOOK.BASで手軽にできる・・・はずだった。
ありゃ?
エラー連発。
しかもキー取得以外のメッセージ受け取ってないじゃん(キャプションバー押しても×ボタン押してもどうにもならない)
なぜかと思いきや・・・
あり?WindowProcもGetKeyNameも間違えてないよなぁ・・・
よくみると。
CallWindowProcの宣言でlParamにByValが抜けてた(^^;)
別にポインタ送るわけでもないし。
道理でドロップサンプルでForm1.hWnd送ると落ちたわけだ。

さりげなくHOOK.BASを修正し、ドロップサンプルも修正版をアップロードすることにして続きを作成。
で、うまくいった。

そのうちJoyBoardに組みこむかな。
でも仮想キーコードだけでなく走査コードorキー名も別に保管しておかないといけないからメンドウだなぁ・・・
テーブル作るわけにも行かないし。(仮想キーコードのどれが拡張ビットをオンにする必要があるかわかんないし)

今度の更新時に「画面モードチェックソフト」をアップするから今のところ更新 ネタがこれだけストック。
ミニソフトでは。
「最適分解能計算ソフト MID Optimizer」
「SFN⇔LFN なソフト SL Converter」

VB講座のほう。
「追尾ミサイルサンプル」
「他アプリデータ取得サンプル」
「キー名取得サンプル」

あとはいざ(?)というときのために。
「半透明サンプル(下のアセンブラのヤツね)」
「SoundTools v1.4」(少し問題あり)

SoundToolsはともかくとしてそれ以外は小粒な内容だなぁ・・・
Micro Warだけひとまず作っちゃえばいいんだけど。
まぁあと8回は持ちこたえられるな(^^;)
でも秋休み(9月)に自宅に帰るとその間プログラムとか組めないしなぁ・・・
ネタの出しっぱなしになるねぇ(^^;)
なんか更新に必死だな。

99/07/30 αブレンド−DrawPrimitive編[★★★★◎◎]
注:あらかじめ言っておきますが、最終的にDrawPrimitiveでの半透明は実力不足の ためにうまく行きませんでした(^^;)

どうも世間一般の(?)もっとも高速なαブレンドのやりかたはやはりD3Dを使った方法のようです。
なんで2Dでαブレンドがサポートされてないのに3Dはサポートされてるんでしょ?
いくらアセンブラでゴリゴリ書こうとハードウェアにはかなわないんですねぇ。
んで、D3DRMはもちろん論外と言うことで使うと言えばD3DIM。
ただExecuteBufferはぜんぜんわからないのでDirectX5で追加されたDrawPrimitiveを利用することにしました。
(実際これを使ってαブレンドをしているゲームをいくつか見たことがあります。)

DrawPrimitiveはまずレンダリング設定をした後に、GDI関数のPolygon並に簡単に三角形とかがかけるんだよね。
本格的に3Dをやるなら機能不足だけど、αブレンドに使うぐらいならOKと。

ただ、あくまでD3DIM。
初期化がメンドイ。

ってなわけで一番とっつきやすそうなDirectX5 SDK Sample -- PPlane(紙飛行機みたいのが飛び交うサンプル)を改良することに。
んで、まずソースをコンパイルしてみようと。

ん?
「IDirect3DDevice2* がシンタックスエラーを起こしました」
とでて、あと100個エラー連発(デフォルトではエラーが100個あるとコンパイルを中止するようになってるため)しました(^^;)
よくよくみたら、インクルードファイルおよびライブラリがDirectX3の時のものだった(^^;)
DirectX6のSDKなんてもってないのでDirectX5から利用。

んで、紙飛行機描画ルーチンの部分を簡単なDrawPrimitiveのルーチンに変えて見る。
ん?グラデーション風に表示されるぞ?
テクスチャーが違う部分で指定されてるのかなと変えて見る。
ありゃ?変わらない。

いいや、αブレンドと。
D3DRENDERSTATE_ALPHABLENDENABLEとD3DRENDERSTATE_COLORKEYENABLEをTRUEにしてみたら・・・ありゃ?背景(四角が平面に並んでる)が消えちゃった。
しかも三角形が半透明じゃなくて数ピクセルおき(?)に並んでるぞ?

三角形ができても四角形ができないとしょうがないからD=B+C−Aのベクトル計算で第4の頂点を求めてDrawPrimitiveでD3DPT_TRIANGLESTRIPを指定してもなぜか描画されませんでした(^^;)
なぜ?

テクスチャーも貼りたかったけど、わざわざサーフェスを1つ追加するコードを書くのがメンドウだったし、サーフェスの属性に何を書けばいいかよくわかんないのでやめました(^^;)

ってなわけでDrawPrimitiveによる半透明はお預け。
せっかくVB→VC→Asmときたら今度は2D→3Dと進もうとしたのに。
(って2Dで半透明やりたいだけじゃん(^^;))
たんに3DやるならD3DRMやった方がいいんだけどね。
しかしD3Dの初期化はなんてめんどくさいんだ。
D3DRMならDirect3Zを使った方がいいね。

今考えればSetRenderStateで描画モードをフラットシェーディングにすればよかったのかもね。
グローシェーディングは平面もピクセル毎に光の計算するしなぁ・・・
そう言えばフォンシェーディングはどうなったんだろうね。


しかしDirectX5 SDKの日本語版ヘルプもってないからなぁ・・・
知らないことをさらに英語で書かれても理解はツライ。
ちなみに、今日パソコンに和訳ソフトがついてることを知ったので試しに DirectX SDKのヘルプをかけてみた(^^;)
しかも語尾が「みたいな〜調」<こんなのつけるなよ

参考にDrawPrimitiveの解説の和訳(「みたいな〜調」「精度優先モード」)でも。

IDirect3DDevice2::DrawPrimitive、およびIDirect3DDevice2::DrawIndexedPrimitive方法は、シングルっていうかー払込催告でルネッサンス以前の芸術作品を描きますっていうかー。可能なみたいなー場合には、これらの機能は、ルネッサンス以前の芸術作品を描くっていうかーために、直接にドライバーへ呼びますってかんじ?でー。
二者択一的に、アプリケーションは、ひとつ(ひとり)ずつ最高点を明示することができますってかんじ?でー。別々に最高点を明示するみたいなーことによってルネッサンス以前の芸術作品を描くっていうかーために、アプリケーションはIDirect3DDevice2::を呼びますみたいなー始まって下さい、そしてルネッサンス以前の芸術作品、および最高点型、IDirect3DDevice2::を明示しますってかんじ?でーそれぞれの最高点、およびIDirect3DDevice2::を明示するってかんじ?でー(べき)最高点ルネッサンス以前の芸術作品を描き終わるってかんじ?でーために、終わって下さい。同様に、別々にインデックスを明示するみたいなーことによって指し示されたってかんじ?でー・ルネッサンス以前の芸術作品を描くってかんじ?でーために、アプリケーションはIDirect3DDevice2::を呼びますってかんじ?でールネッサンス以前の芸術作品、および最高点型、IDirect3DDevice2::をBeginIndexであって明示しますそれぞれのインデックス、およびIDirect3DDevice2::を明示するってかんじ?でー(べき)インデックスルネッサンス以前の芸術作品を描き終わるっていうかーために、終わって下さい。

ただでさえわかんないのにこんな解説されたくない(^^;)
しかも「DrawPrimitive」で「ルネッサンス以前の芸術作品を描く」ってなんなんだ
「call = 払込催促」ってなんなんだ。
プログラム用語辞書もつけてくれ。

参考に自分なりの和訳。

IDirect3DDevice2::DrawPrimitiveとIDirect3DDevice2::DrawIndexedPrimitiveの メソッドは1回のコールで単純図形(Primitiveってこれでいい?)を描画する。
可能なら、この関数は直接ドライバーに図形を呼び出して描かせる。
排他的に(?)アプリケーションは頂点群を指定する。ここに頂点を指定することで図形を描画する場合、IDirect3DDevice2::Beginを実行して頂点と図形のタイプを指定し、IDirect3DDevice2::Vertexで頂点を指定し、IDirect3DDevice2::Endで図形描画を終了する。似た方法で、個々にインデックス付けされた図形を描画する場合、アプリケーションはIDirect3DDevice2::BeginIndexを呼んで頂点と図形のタイプを指定し、IDirect3DDevice2::Indexでインデックスを指定し、IDirect3DDevice2::Endで図形描画を終了する。

くらべてみてどうでしょ?
参考に原文。

The IDirect3DDevice2::DrawPrimitive and IDirect3DDevice2::DrawIndexedPrimitive methods draw a primitive in a single call. When possible, these functions call into the driver directly to draw the primitive.

Alternatively, the application can specify the vertices one at a time. To draw a primitive by specifying the vertices individually, the application calls IDirect3DDevice2::Begin and specifies the primitive and vertex type, IDirect3DDevice2::Vertex to specify each vertex, and IDirect3DDevice2::End to finish drawing the primitive. Similarly, to draw an indexed primitive by specifying the indices individually, the application calls IDirect3DDevice2::BeginIndexed and specifies the primitive and vertex type, IDirect3DDevice2::Index to specify each index, and IDirect3DDevice2::End to finish drawing the primitive.

99/07/27 MIDファイル解析編[★★◎◎◎]
最近ホームページの更新ネタが尽きてきたんでミニソフトの作成を少しやってるところ(^^;)。

そろそろまともなミニソフトを作りたいなぁと言うことでMIDファイルの解析の経験を生かして最適分解能計算ソフトなる物を作るようにした。

単純にDeltaTimeの最大公約数を計算することで最小の分解能を求めようと言うもの。
んで作成・・・
トラック解析は実はDeltaTime計算→イベント解析だけで済むのでだらだらと。

んで、計算させると・・・
うーん、あってるかどうかわからないんで簡単なMIDファイルを作成。
ん?
分解能960で4分音符を480間隔で置いたから最大公約数は480となり、最適分解能は 2と出るはず。
なのになぜか「960/8=120」だと。
ほかのファイルでも「こんな値でないだろ」って値の連発。

SoundToolsのソースと見比べても・・・
うーん、間違ってるところはないだろうと思うんだけど・・・

んでよくみたらDeltaTimeの計算で
a = a*128 + b
となってるし。
a = a*128 + (b mod 128)
だね(^^;)
それだけ(^^;)

ただ、さらに問題が。
なぜかVB IDE環境下だとうまく行くのにEXEにするとオーバーフロー・・・
一応上のbがbyte型で指定してあるのが、マズイのかなということで、そこだけ Longにしたらうまく行ったけど。

久々に使えるかも?なソフトを作ったのでVectorにアップするかも。

ソースはつけるけど、MIDファイルの構造を知るにはかなりキビシイかもね。
イベントの部分はSelect Case文でオフセットを進めるだけであとはかっ飛ばしてあるし。
まぁ分解能計算専用のソフトだし、そのおかげでP-Codeでもそれなりな速度が出てるんで。
あと、C++風にインクリメントやポインタっぽい扱いをしたかったんで、(その方がラクだから)AddOffsetとかそんな感じの関数を作ってあるからなおさらVBerにはわかりにくいかと。

99/07/20 ブレンド編の補記
せっかくなので下のコードを24bitでも試してみました。
単純にデータのピッチ数を3倍にしてDLLに送ればいいんで。

結果は・・・
VB-8FPS Asm-23FPS C-18FPS
うーむ。
まあバイト数が3倍になってると言うことで速度が3倍弱になるのはしょうがないんだけどさ・・・
どっちにしてもこれは使えるレベルではないでしょう。

なんかαブレンドためにDirect3DIMでPrimitiveModeとか使ってるのとかあるけど、あっちの方がよっぽど速くていいね。
FPUの命令の使い方がわかれば、今度は回転に挑戦するかも。

どうもどっかで聞いた話だとVRAM→SYSMEMは非常に遅いけど、SYSMEM→VRAMはそうでもないらしい。
まぁそうでもないとあの速度は出ないよなぁ・・・>いろんなデモ

99/07/16 テーブル利用αブレンド編[★★★◎◎◎◎]
さてさて、本題の(?)αブレンドに挑戦です。
ただし、16bitモードはその特性上ビットシフトが必要となるために非常に遅くなるし、24bitモードはバイト数が増えるし速度が一気に遅くなるので8bitということで。
んで使うのがテーブルを利用したブレンド。
あらかじめブレンドした場合の色を計算しておいてさらにパレット番号として用意しておく方法です。
ここでは次の方法で半透明(ブレンド率50%)のテーブルを準備します。
'合成テーブル作成 a-Src b-Dest
'高速化のためにTB(a,b)=TB(b,a)とすることで計算量半減
For a = 0 To 255
For b = a To 255
temp = RGB(.palPalEntry(a).rgbRed \ 2 + .palPalEntry(b).rgbRed \ 2, _
 .palPalEntry(a).rgbGreen \ 2 + .palPalEntry(b).rgbGreen \ 2, _
.palPalEntry(a).rgbBlue \ 2 + .palPalEntry(b).rgbBlue \ 2)
TByte = GetNearestPaletteIndex(Pal, temp)
If a = TC Then TB(a, b) = b Else TB(a, b) = TByte
If b = TC Then TB(b, a) = a Else TB(b, a) = TByte
Next
Next
このGetNearestPaletteIndexが遅いんだよね。
だから256*256パターンやると5秒はかかるから半分だけ計算をするようにしてるというわけ。
さて、スプライトでは640*480*16bitでスプライト200個だと
VB-51FPS BitBlt-62FPS Asm-126FPS C-81FPS
となるのは以前書いた通り。
んでテーブルによるブレンドは・・・
VB-27FPS Asm-78FPS C-57FPS
アセンブラだけは辛うじて許容範囲かも。

DirectDrawの場合はこれを参考にしていいのだろうか・・・
ブレンドはSYSTEMMEMORY上でのサーフェスで行えばいいけど。
SYSTEMMEMORYからVIDEOMEMORYにBltしてどのぐらいの速さになるかだね。

またもやDLL作成のコード公開。
//ブレンド-アセンブラ
extern "C" VOID FAR PASCAL DIBTableBlt
(byte* DSur,int DPitch,byte* SSur,int SPitch,int LB,int HB,byte* Table)
{
    int a,b;

    //a-y座標
    //b-x座標
    _asm
    {
        mov a,0
        ;ループ開始
yloop:  mov b,0
        ;ポインタ計算
        ;Destラインスタート位置 ebx= DSur + DPitch*a
        ;Srcラインスタート位置  ecx= SSur + SPitch*a
        mov eax,DPitch
        mul a
        mov ebx,eax
        add ebx,DSur
        mov eax,SPitch
        mul a
        add eax,SSur
        mov ecx,eax
        ;edx=Table[al][dl]=Table+al*256+dl=Table+ah+al=Table+ax
        ;8bitシフト高速化のためdlでなくahをいじる
xloop:  mov al,[ecx]
        mov ah,[ebx]
        movzx eax,ax 
        mov edx,Table
        add edx,eax
        mov al,[edx]
        mov [ebx],al
        inc b
        mov edx,LB
        cmp b,edx
        jae ychk
        inc ecx
        inc ebx
        jmp xloop
ychk:   inc a
        mov edx,HB
        cmp a,edx
        jb yloop
    }
}


//ブレンド−C言語
extern "C" VOID FAR PASCAL DIBTableBltC
(byte* DSur,int DPitch,byte* SSur,int SPitch,int LB,int HB,byte* Table)
{
    int a,b;
    byte* DS;
    byte* SS;

    for(a=0;a<HB;a++)
    {
        DS=DSur+a*DPitch;
        SS=SSur+a*SPitch;

        for(b=0;b<LB;b++)
        {
            *DS=*(Table+(int)(*DS)*0x100+(int)(*SS));
            DS++;
            SS++;
        }
    }
}
FPUは486DX2あたりならついてるんだよね?
回転描画も考えてみるかなぁ・・・

99/07/13 アセンブラ挑戦中級者編[★★★★◎◎]
昨日英語の試験が終了したので再びアセンブラに挑戦。
VRAMアクセスの遅さにイラついて今回はDIBSectionで。
αブレンドは置いといて(256色モードなので)単なるスプライトってことで。
(テーブル作ればいいんだけど)

ダダダとコードを組む。
レジスタがeax,ebx,ecx,edxだけだと足りないので急遽
int a,b;
に手伝ってもらった。
変数をそのまま利用できるのはインラインアセンブラの利点だね(^^;)
(後で気がついたけどesiとか使えば良かったのか)
どうもアセンブラコードを見てみると関数内の変数は
[ebp-4]とか[ebp-8]のアドレスに来るんだね。
同様に引数は[ebp+4][ebp+8]とかになる。(もちろんインラインアセンブラなら引数名を直接利用できるよん)

思ったんだけど強引にインラインアセンブラでebpを任意に動かせるコード作れば単なるバイナリファイルにあるデータをアセンブラのプログラムとして使えるかな?
そうすればそのDLLだけであとは外部ファイルを付け足すだけでいくらでもアセンブラをVBからつかえるじゃん。
(もちろんニーモニック→アセンブラなプログラムは必要だけど。)
ebpをpushしてバイナリファイルのデータのポインタに移してあとでpopすればOKか?
でもVCのマニュアルには「ebpはなんらかの理由が無い限り保持」ってことは理由があればOKか?
検討の余地あるかも。

んでコード完成。
んでVBの既存のDIBSectionサンプルに組みこむ。

うーん、うまく行かない・・・
エラー連発。
しょうがないからCreateDIBSection時に返されるhMemでなくVB内で取ったバッファにアセンブラで書きこんでRtlMoveMemoryだと・・・うまく行く。
なぜ?

ってなわけで調べてみたら・・・
API宣言がByRefとByValが間違ってた(^^;)
ポインタとその参照先をグリグリいじるタイプのコードでは致命的。

ってなわけで完成!
では速度を見る。
(DIBSectionはいずれも8bit)
640*480*16bitでスプライト10個だと
VB-360FPS BitBlt-710FPS Asm-550FPS C-490FPS
うーむ、VBよりよっぽど速いけどハードウェア利用のBitBltにはかなわないか。
まぁVB<C<ASMってのは予想通りだね。
では数を増やせばいいかな?
640*480*16bitでスプライト200個だと
VB-51FPS BitBlt-62FPS Asm-126FPS C-81FPS
これは思いっきり効果でたね(^^;)
BitBltと違って1回しか描画しないのが強みか。

まぁBitBltは色数とともに急激にトロくなるからねぇ・・・
ってなわけで640*480*24bitで200個だと
VB-50FPS BitBlt-44FPS Asm-118FPS C-77FPS
まぁ最終的にDIBSection→ピクチャーボックスの描画がやはりBitBlt使ってるにしてもアセンブラ圧勝!

参考にDLL作成に使ったコードを公開!
extern "C" VOID FAR PASCAL DIBBlt
(byte* DSur,int DPitch,byte* SSur,int SPitch,int LB,int HB,byte TC)
{
    //アセンブラによるスプライト
    //DSur -- 描画先ポインタ(座標を考慮した値を送る)
    //DPitch -- 描画先ラインピッチ
    //SSur,SPitch -- DSur、DPitchの描画元版
    //LB -- 幅(バイト)
    //HB -- 高さ
    //TC -- 透明色パレット番号

    int a,b;

    //a-y座標
    //b-x座標
    _asm
    {
        mov a,0
        mov dl,TC
        ;ループ開始
yloop:  mov b,0
        ;ポインタ計算
        ;Destラインスタート位置 ebx= DSur + DPitch*a
        ;Srcラインスタート位置  eax= SSur + SPitch*a
        mov eax,DPitch
        mul a
        mov ebx,eax
        add ebx,DSur
        mov eax,SPitch
        mul a
        add eax,SSur
xloop:  mov cl,[eax]
        cmp cl,dl
        je xle
        mov [ebx],cl
xle:    inc b
        mov ecx,LB
        cmp b,ecx
        jae ychk
        inc eax
        inc ebx
        jmp xloop
ychk:   inc a
        mov ecx,HB
        cmp a,ecx
        jb yloop
    }
}

extern "C" VOID FAR PASCAL DIBBltC
(byte* DSur,int DPitch,byte* SSur,int SPitch,int LB,int HB,byte TC)
{
    //Cによるスプライト(C++じゃないね)
    int a,b;
    byte* DS;
    byte* SS;

    for(a=0;a<HB;a++)
    {
        DS=DSur+a*DPitch;
        SS=SSur+a*SPitch;

        for(b=0;b<LB;b++)
        {
            if(*SS != TC) *DS=*SS;
            DS++;
            SS++;
        }
    }
}
ってなところ。
なんか似たような命令ばっかり使ってるね。
まぁ最近のCPUで増えた命令はそれなりに特殊だしそりゃそうか。
ここで使ってるような命令は基本的な命令ばっかりだから80386以上なら多分動くでしょう。
Win95は486以上が基本的な動作条件だしね。
でも本格的に使うんならFPU絡みのコードを作りたいねぇ・・・(sinとかcosも1つの命令で返してくれるし。)
486DX4あたりなら十分FPU入ってるんだっけ?

前回jzで「オペランドないよん」エラーが起きたのは、
cmp eax,HB
jz sinit
のようなコードを書いてて、常に「HB」とか「LB」のような変数の後にエラーになっていたのを発見。
(それ以外の場所だとエラーは起きないし、この場所だと他のジャンプ命令も不可になる)
cmpは変数同士の比較はできないんだね。

それでしょうがなく上では
mov ecx,HB
cmp a,ecx
jb yloop
としてます。
レジスタと変数の比較でないとダメなのか。

しかし・・・アセンブラ組んでるとVBが落ちたときにDLLを「デバッグモード」で組んでようが「リリースモード」で組んでようがアセンブラが出るから関係ない(^^;)
ラクでいいねぇ。
もちろんニーモニックだよ(^^;)
ちゃんと親切に「mov ebx,dword ptr [ebp+4]」みたく出てくれるし。
まぁバイトの配列が並んでるのを見たってどうしようもないからね。

うーむ、DirectXもSYSMEM→SYSMEMで作業をしておいて最後だけあきらめてDirectDrawSurface::Bltすればいいのか?
VRAM←→SYSMEM間で何度もメモリ操作すると激遅(下の日記みてもわかるっしょ)なんでそれはしたくないし。

このサンプルはホームページ公開のネタが尽きたときに公開します(^^;)

99/07/11 アセンブラ挑戦初心者編[★★★◎◎]
7/10の時点。
とあるサイトでデモ(作者はメガデモとは言っていない)をDLしました。
デモってのは体験版とかそう言うデモじゃなくて、プログラムや絵・音を駆使して高度な演出をしたCG(と言うのも微妙だな)です。

んで、見てみた。
おお、すごいね。
DirectX使ってるみたいだけど・・・
アセンブラ使ってるんだねぇ・・・

ってなわけでアセンブラをやってみたくなった。(影響受けすぎ)

ひとまず初心者と言うことでCASLに挑戦。
VectorにはいくつかオンラインのCASLコンパイラ+COMETシミュレータがあるからね。
んで複数のソースを見てみた。
うーん、めんどうだね。
ってなわけで1時間ぐらいして次のプログラムを書いた。
; 1-10の数を足すプログラム
;BASIC風だと For GR1 = MINNUM to MAXNUM step STEP

        START
        LD GR1,MINNUM
        SUB GR1,STEP
LOOP    ADD GR1,STEP
        ST GR1,RESULT
        ADD GR2,RESULT
        CPA GR1,MAXNUM
        JMI LOOP
        ST GR2,RESULT
EXIT
STEP   DC 1
MINNUM DC 1
MAXNUM DC 10
RESULT DS 1
        END

ADD GR2,GR1ができないのがつらいけど。
確認してもらったところGR2の初期化とプログラム名をいれればいいっぽそう。

んで、夜にIntelのサイトからニーモニックのマニュアルをDL!
中巻をDLしたら1.1MB(^^;)

んで7/11。
αブレンドのコード作成・・・
しまった(^^;)
メモリアドレス先を参照する命令がわからん(^^;)
しかも
jz ablend
ってやったらチャンとablendってラベルがあるのに「オペランド2が変」だと。
「2」ってことはjzも変なのかな?
しょうがないからひとまずCで組む。

//50%αブレンド
#include 

extern "C" VOID FAR PASCAL DTAlphaBlt(byte* FSur,int FPitch,byte* DSur,
int DPitch,byte* SSur,int SPitch,int LB,int HB,int BR)
{
    int a,b;
    byte* DS;
    byte* CS;
    byte* FS;

    for(a=0;a<HB;a++)
    {
        FS=(byte*)((int)FSur+a*FPitch);
        DS=(byte*)((int)DSur+a*DPitch);
        CS=(byte*)((int)SSur+a*SPitch);
        for(b=0;b<LB;b++)
        {
            if(*CS) *FS = (*DS + *CS)/2;
            FS++;
            DS++;
            CS++;
        }
    }
}

ほんとはFSとDSは同じでもいいんだけど・・・
VRAMの操作はメチャクチャ遅いです。
FSをDSで置きかえると、72*72のキャラで60FPS・・・
VRAMから読みこんでさらに書きこんでるからね。
同じプログラムをVBで組んだやつでも70FPSいくのに・・・
まぁあれはメモリをムダ遣いして、VRAMへ1回書きこむだけにしてるからね。
んで、DSは通常のメモリ、FSはVRAMってことにしてやったら・・・120FPS(リフレッシュレートが120Hzだからそれ以上行くのかも)
ただ、ここではブレンド率50%にしてるから高速だけど、これを10%単位で任意にしたら一気に落ちた(^^;)>80FPS

どっちにしてもVRAM使ってるのが間違いかも。
サーフェスをシステムメモリ上に置けるようにDTVerUPするかな(^^;)
でもシステムメモリ→VRAMだとDirectDrawSurface::Bltって速いのかな?

んで、やはり高速化にアセンブラはいるだろうとアドレス参照先のデータを操作する関数を探す。
うーん、PDFファイルで563ページ・・・見る気もうせるな。
ってなわけでヒントを求めてIntelでマニュアル上巻800KBをDL。
下巻は4.8MBもあるし応用編っぽかったからDLしてない・・・

んで見ると。。。String系命令で行けるたいね。
なんかStringって言うのは違和感があるけど。

あとjzを解決しないと組めないな。


一覧へ戻る