なんだか雲行きの怪しい雑記帖

ぐだぐだ日記とメモと,あと不定期更新

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【HSP】【DXLib】【Effekseer】HSPからエフェクト再生ランタイム「Effekseer」を使えるプラグインを作った話

もうなんかタイトルが邪悪.

前置き

表題の通りですが,なぜそれをやったのか(やってしまったのか)という話,モチベーションについて.

代替品がない

そういえばHSPってゲームでもエフェクトだそうとすると,パーティクル一つ一つの計算処理を全部手でコード書かないといけなくなるのですが,元々多くの変数を効率よく扱うための仕組みがないので凄い辛いんですよね.
ではその部分を分担してくれるライブラリがあるのかと言うと,現状フラッグシップなものが無い.(※個人の感想です)

近いものとして挙げられるのが「ParticleTestTool」,これは本当に近い,でも現状E3Dの状況を鑑みるととても惜しい.(今でもE3Dは従来通りのライセンスのまま使えますが,公式に同梱されなくなったことと,メンテされていなこと,更にはhgimg4がリリースされたという背景があるため,なんとも言えないところですね)

その他に挙げられるものが「SpriteStudio」だけど,これはHSPでもとりあえずアニメーション情報読み取って表示できる機能で,大量のスプライトを扱おうとするとHSPの処理コストからリアルタイムは結構きつそう.

「d3module」は素晴らしいけど,一つ一つのエフェクトを作るのに必要な職人芸的なパーティクルの挙動調整へ費やす時間が大変.
また,一個一個エフェクトを調整し終えることができたとして,そもそも大量のエフェクトを一括で管理するコードを”HSP上で”実装するのは現実的に難しいでしょう.

元々HSPはある程度粒度が大きいライブラリを揃えないと使いづらい

HSPは言語的(主に文法的)に,HSP側で多くの機能単位を内包しておくことが難しい,と思ってます.

結構具体的に説明するのが難しいんですが,誤解を恐れずにざっくばらんに言うなら「hgimg3よりE3Dの方が地形との当たり判定まで面倒見てくれるから3Dゲーム作るのは楽」とかそういう話です.

根本的にHSPは抽象化ができないので,汎用化されたデータであってもアルゴリズムが適用できないあたりが原因だと思ってますが,よく分からない話に突っ込みそうなので置いておきます.
ひとまずここでの主張は,「エフェクトを一個再生したい」という実装者の要求に対して,「自分でパーティクル一つ一つの挙動をコードとして実装する」のではなく「予め別のツールで作っておいて,データとして保存したものを”再生する”と命令するだけで画面に表示されれば楽ですよね」と,そんな感じです.
 (あるいは,もしOBAQに描画する機能が存在せず,自分でOBAQ内のオブジェクト全部をイテレートして自前で描画するしかできなかったら辛いよね,とか.)

エフェクトって映える

聞いた時ちょっと至言だなって思った.

ちなみに今回のEffekseer組み込みを行うと,次のようなエフェクトは「エフェクトファイル読み込み」→「再生」命令でサックリだせる.
effekt_Intro2.pngeffekt_Intro1.png
(Effekseer本体付属のサンプルを本プラグインを使って表示したところ)

その他どういうエフェクトがあるかはEffekseer公式の紹介ページを参照とのこと.

上記画像見てテンションが上がらない場合,あまりこのライブラリを使うメリットがないような気がする,そういう方には申し訳ない….
記事構成

本記事に含まれる内容は2つありますので,前半と後半の2部構成になっております,適宜自分の興味のあるところだけかいつまんで下さい.

●前半:HSPからDXライブラリ+Effekseerを使ってエフェクトをだすまで
題名の通り,DXライブラリの初期化とEffekseerを使ってエフェクトをだすまでに使う命令の解説となります.
本記事の本体とも言える内容.

●後半:Effekseer組み込みプラグインの立ち位置,内部で動作する補完的な処理について
プラグイン内部でEffekseerに投げている処理フローに踏み込みんでいきますが,そもそもラップだけして何もしてないようなもんなのであまり長くはならない予定.

どちらにも属さないけど早めにだせよって言われるダウンロードリンクはすぐ次.

ダウンロード

 2016/10/23 ver.alpha DropBox GoogleDrive
  ・エフェクトインスタンスのルートのみ停止するefkStopRootを追加
  ・3Dと鉛直方向に互換性があり、画面中心がゼロ点となるような射影を設定するefkSetOrtho3DProjを追加
  ・サンプルの一部が、一部のGPUだと正常に描画されていなかった問題を修正

 2016/08/01 ver.alpha DropBox
  ・Effekseer ver120に更新
  ・座標系(左手系、右手系)を選べるようにefkCoordinateSystemを追加
   引数を指定して呼び出せば値の設定、何も引数を渡さなければ現在設定されている座標系を返します
   (getter/setter兼用の設計です)
   座標系の定数として EFK_COORDINATE_SYSTEM_LH と EFK_COORDINATE_SYSTEM_RH を追加

2016/05/22 ver.alpha DropBox
  ・Effekseer ver110に更新
  ・無効なハンドルを渡した際にヌルポインタアクセスする問題を修正
  ・バイナリリソースを登録した状態でネットワーク機能を使うとヌルポインタアクセスになる問題を修正
  ・efkRegMemFileは元のメモリ領域のコピーをとり、HPI側でメモリを管理するように修正
   従来は元のメモリ領域そのものを覚えていたが、HSPではメモリ領域の管理が煩雑になるためそれを緩和する仕様変更
   従来通りメモリ領域をHSP側と共有する動作はefkRegMemFilePtrとして追加

 2015/12/26 ver.alpha 初版 DropBox

●前半:HSPからDXライブラリ+Effekseerを使ってエフェクトをだすまで

なるべく専門的な知識が必要になる箇所は省きますし,サンプルコード多めで命令毎の意味が分かりやすいように書きたいと思います.
ただ,HSPからDXライブラリ使うところは飛ばしますので,その辺は私の前の記事を参照して補完お願いします.

Effekseerの初期化

どのような外部プラグインも基本は初期化が必要です,例に漏れず本プラグインも初期化が必要です.

初期化の際に必要なのはDirect3D9のデバイスハンドルのポインタLPDIRECT3DDEVICE9というものなのですが,これはDXライブラリから「GetUseDirect3DDevice9」関数を通して取得可能です.
DXライブラリ初期化後でなければ有効なデバイスハンドルが取得できないことに注意してください.
    // DxLibの初期化
dx_DxLib_Init
if ( stat == -1 ) : dialog "初期化エラー"

dx_SetDrawScreen DX_SCREEN_BACK

// d3dデバイスの取得
dx_GetUseDirect3DDevice9
d3dDevice = stat

// エフェクトエンジンの初期化
efkInit d3dDevice

プラグインの初期化はこれで終わりです.
※本記事ではDXライブラリ内の関数はプレフィックス「dx_」がついています.
 これはDXライブラリがエクスポートする関数名そのままを使っているからで,名前が違うだけで「dx_」がついてない関数と同一のものですので,適宜読み替えて下さい.

なお,「efkInit」の第二引数は省略可能ですが,描画したいパーティクルの最大数を明示的に指定することも可能です.
省略された場合はデフォルト値で「2000」が入りますが,大量のパーティクルを表示したい場合は大きめの値に設定しておくことをおすすめします.

エフェクトを画面上にだすまでの流れ

さて,初期化は終わりましたが,ここでエフェクトを画面にだすまではどういった流れになるのかについて書いておきます.
特に一般的なライブラリに比べてイレギュラーな処理は存在しませんが,どういった流れで処理が行われるかが分かってると理解しやすいですからね.

1. エフェクトファイル「.efk」から「efkLoad」によってエフェクトをロードし,ロードしたエフェクトに対応するID(エフェクトID)を得る
2. 1でロードしたエフェクトIDを指定して,「efkPlay」によって実際にエフェクトのインスタンスを生成し,エフェクトインスタンスのIDを得る
3. 2で得られたエフェクトインスタンスのIDに対して,位置やスケール,回転角度,目的位置を指定する
4. 1フレームに一回,全エフェクトインスタンスの更新を行う「efkUpdate」を実行する
5. 描画する際は「efkBeginDraw」,「efkKickDraw」,「efkEndDraw」の一連の命令を実行する
6. 必要なくなったエフェクトは「efkUnload」で破棄する

このような感じです.

2でエフェクトは一度ファイルから読み込まれますが,これはエフェクトの挙動などを示した云わばテンプレートのようなものであり,読み込んだだけではエフェクトは表示されません.

エフェクトを再生したい場合は「efkPlay」を実行する必要があります.
裏を返すと発生させたい個数だけ「efkPlay」を実行すればよく,テンプレートとなるエフェクトは一個だけしか存在しないため,メモリにも優しい実装となっていますし,ID指定なのでパフォーマンスも良くなります.

描画する際は上述した内容よりもっと制約条件があるのですが,ひとまず流れとしてはこれぐらいでいいでしょう.
次節からそれぞれの処理内容について見ていきます.

エフェクトファイルのロード

ここは特殊な処理はありません,ファイルパスを指定して読み込むだけです.
    efkLoad effectFilePath
effectId = stat// 読み込んだエフェクトのIDを覚えておく

なお,ロードに失敗した場合は0が返ります.

エフェクトインスタンスの生成

ここも特殊な処理はありません.
エフェクトインスタンス毎にちゃんとID(というかハンドル)が存在することには注意してください.

    efkPlay effectId
effectHandle = stat// エフェクトインスタンスのID(ハンドル)


エフェクトインスタンスに対する設定

設定したいところにだけ設定する.
設定に使うのはエフェクトインスタンスのIDであることには注意.

可視
    efkSetVisible effectHandle, boolean

ポーズ
    efkSetPause effectHandle, boolean

位置
    efkSetPos effectHandle, x, y, z

回転
    efkSetRot effectHandle, xRot, yRot, zRot// オイラー角,ラジアン単位

スケール
    efkSetScale effectHandle, xScale, yScale, zScale

目的位置
    efkSetTargetPos effectHandle, xTarget, yTarget, zTarget


エフェクトの更新

特に追加の説明が必要なさそうなところ.
一応,全エフェクトインスタンスに対して実行されるため1フレームに一回だけ呼べばOKです,呼びすぎはむしろダメです.
    efkUpdate

なお,第一引数は省略可能でデフォルトでは1.0が入りますが,ここに2.0とか入れると再生スピードが2.0倍になります.

エフェクトの描画

説明が凄い要るところです.
エフェクトの描画自体はEffekseer内部で行ってくれますが,描画コンテキストはメインの描画エンジンと共有するため,例えば「半透明描画は可能か」「デプスは使用するか」といったレンダーステートの管理を適切にEffekseer用に設定し,描画後は元のコンテキストに戻るように設定し直す必要があります.

まずEffekseer用に必要なレンダーステートとして重要なところは,(Effekseerランタイム組み込みリファによると)「半透明描画が可能」で「デプスバッファが使用可能」であることです.

後者はDXライブラリ的には「SetUseZBuffer3D」と「SetWriteZBuffer3D」の2つで良いのですが,前者は明示的に設定することができません.
そこで,何かしら意味がない半透明描画を実行することで強制的にレンダーステートを設定させます.
一番お手軽なのは何らかのダミーテクスチャを描画することです.
        // Effekseerが描画可能な描画設定を流し込む
dx_SetUseZBuffer3D 1
dx_SetWriteZBuffer3D 1

dx_SetDrawBright 255, 255, 255
dx_SetDrawBlendMode DX_BLENDMODE_ALPHA, 0
dx_DrawExtendGraph 0, 0, 2, 2, dummyScreen, 1
dx_RenderVertex

Effekseer用レンダーステート発行部分を修正@2016/10/23
強制的に半透明実行するレンダーステートを発行するため、何らかのダミーテクスチャを描画するステップを追加しました。
使っているグラボによってはウィンドウ時このレンダーステート発行が必要ない事があるみたいですが、フルスクリーンにするとエフェクトがでない! 等謎の現象を確認しています。
ので、必要なステップとして追記した次第です。


描画自体は,流れのところで説明したままです.
    // エフェクト描画 : これは1セット
efkBeginDraw
efkKickDraw
efkEndDraw


描画後,DXライブラリのレンダーステートに戻すには,次のようにします.
    // DxLibの設定をリロード
dx_RefreshDxLibDirect3DSetting


一つのエフェクトを描画する

さて,ではこの辺で一旦コードを繋ぎ合わせて,一つのエフェクトを読み込み,描画するところまでのコードを書いてみましょう.

ここで何かしらエフェクトを表示する際に必要となるefkファイルを置いておきます.
サンプルコードを見る前にここで一旦自分なりに挑んでみてもいいですし,すぐにサンプルコードを見るも良いでしょう.
案外,DXライブラリのコードとの兼ね合いで,すんなりコードが書けてエフェクトがでるっていうのは難しいと思います.(この辺なんとかならんかな~)

テスト用efkファイル:DropBox

では,サンプルコードは次.
#include "dxlib_header.hsp"
#include "effekseer.hsp"

screen 0, 640, 480
title "effekt sample"

// ウィンドウモードをON(OFFの場合フルスクリーンになる)
dx_ChangeWindowMode 1

// DxLibで描画するウィンドウをHSPのウィンドウに変更
dx_SetUserWindow hwnd
dx_SetUserWindowMessageProcessDXLibFlag 0

// effekseer連携の際の設定
dx_SetUseDirect3DVersion DX_DIRECT3D_9EX// we use dx9ex
dx_SetChangeScreenModeGraphicsSystemResetFlag 0

// DxLibの初期化
dx_DxLib_Init
if ( stat == -1 ) : dialog "初期化エラー"

dx_SetDrawScreen DX_SCREEN_BACK

// d3dデバイスの取得
dx_GetUseDirect3DDevice9
d3dDevice = stat

// 裏画面の用意:ダミーテクスチャとして使用
dx_MakeScreen 4, 4, 1
dummyScreen = stat

// エフェクトエンジンの初期化
efkInit d3dDevice

// エフェクトのロード
efkLoad "test_bs.efk"
effectId = stat

efkPlay effectId
effectHandle = stat

onexit goto *OnExitApp

repeat
// 裏画面を描画ターゲットに指定
dx_SetDrawScreen DX_SCREEN_BACK
dx_ClearDrawScreen 0

// *-------------------------------------------------------------*
// エフェクト描画要点

// Effekseerが描画可能な描画設定を流し込む
dx_SetUseZBuffer3D 1
dx_SetWriteZBuffer3D 1

dx_SetDrawBright 255, 255, 255
dx_SetDrawBlendMode DX_BLENDMODE_ALPHA, 0
dx_DrawExtendGraph 0, 0, 2, 2, dummyScreen, 1

dx_RenderVertex

// エフェクト描画 : これは1セット
efkBeginDraw
efkKickDraw
efkEndDraw

// DxLibの設定をリロード
dx_RefreshDxLibDirect3DSetting

// *-------------------------------------------------------------*
// エフェクト更新
efkUpdate

// *-------------------------------------------------------------*
// 裏画面を表画面へ転送
dx_ScreenFlip
dx_ProcessMessage
await 0
loop
stop

*OnExitApp
efkDestroy
end

どうでしょうか,書けた or 簡単に想像できたでしょうか?
なお,上記サンプルは実行するとこんな感じで一回エフェクトが再生されてそのまま真っ暗です.
effekt_sampleExecuted.png

カメラ,プロジェクションの設定

前節までで一応再生まではできました.
ただ,カメラの設定等は何もしておらず,実際にアプリに組み込む際にはまだ機能的には足りません.
ここからは,少し掘り進んだ話題を扱います.

カメラ

カメラとして設定できる命令は2つありますが,難しさを考えると実質1つです.

1つは「efkSetLookAtCamera」で,カメラ位置,注視点位置,カメラの上ベクトルを渡すことで自動的にビュー行列を設定してくれます.
    efkSetLookAtCamera cameraX, cameraY, cameraZ, atX, atY, atZ, upX, upY, upZ


もう1つは「efkSetViewMtx」で,ビュー行列を直接設定できます.

プロジェクション

プロジェクションを設定する命令は3つで,実質2つです.

1つは「efkSetPersProj」で,パースのついたプロジェクション行列を設定してくれます.
    efkSetPersProj fov, aspect, near, far

fovは「Field of View」(画角)のことで,ラジアン単位で指定します.
アスペクト(画角比)とニア,ファーは分かると思うので割愛.

次の1つは「efkSetOrthoProj」で,パースのつかないプロジェクション行列(正射影)を設定してくれます.
    efkSetOrthoProj width, height, near, far


最後は「efkSetProjMtx」で,プロジェクション行列をそのまま設定します.

サーバー機能の有効化

Effekseerにはツールとランタイムがネットワーク越しで自動的に連携をとり,ツールで編集している内容をリアルタイムでアプリ内で表示するエフェクトに適用できる機能があります.
これにより,エフェクトの細かい調整がアプリを起動したままできるため非常に便利です.

本プラグインからサーバー機能を有効化するのは簡単で,「efkInitServer」で初期化するだけです.
    efkInitServer port


ネットワーク越しで通信を行うため,ポート番号を指定します.
省略した場合はデフォルト値で「60000」が使われますが,ツール側もデフォルトが「60000」なので,デフォルトのままでもいいかもしれません.

サーバー機能を有効化した後にロードされたエフェクトは,ツール側で同名の「efkproj」編集中の場合,その内容がリアルタイムで適用されます.
例えば,「test.efk」をアプリ側で読み込んで使用していた場合,ツール側で「test.efkproj」を編集中であったら,その内容が更新される,という具合です.
サーバー機能を有効にしても自動でツール側から接続するわけではないので,ツールからのメニューバーからウィンドウ>ネットワークを開き,接続を押して下さい.

歪みエフェクトの有効化

この機能は歪みエフェクトを使用する際に必要です.
歪みエフェクトは図のように背景の一部が歪んで表示されるエフェクトのことです.
effekt_distortionSample.png
サンプルエフェクトとしても一つ入っていますので,実際のエフェクトはそちらを見て確認してください.

さて,歪みエフェクトを使用する際,ネットワーク機能と同様最初に一回だけ初期化の命令「efkInitDistortion」を入れるだけで有効化されます.
    efkInitDistortion width, height


引数として渡すのは画面領域のサイズで,省略できません.
ここで指定したサイズにより画面領域のコピーテクスチャを生成するので,実際の画面領域より小さい領域にすることは有効ですが,大きくするのは処理が増えるだけで意味ないです.

画面領域より小さくすることで処理を稼ぐことができると思いますが,単純に解像度が下がるので用法用量は適切に.
次に比較画像を乗っけておきます,よっぽど処理に詰まってない限り解像度は下げない方がいいでしょうね.
effekt_distortionQuat.pngeffekt_distortionFull.png
左:4分の1画質
右:フル画質

縮小,拡大にはリニアサンプリングを行いますが,サイズが同じ場合はポイントサンプリングに切り替わります.
この辺は需要があるなら色々オプション追加するかも.

バイナリリソースの登録

ここまででエフェクト再生に関する事項はほぼ全て網羅されています.
この節では,ゲームなど最終的にリソースをバイナリ化して持っておきたい時に使用する,バイナリリソースの登録についてです.

と言っても非常にインタフェース自体は簡便化されており,バイナリリソースの登録・削除は「efkRegMemFile」と「efkUnregMemFile」だけです.
    // バイナリリソースの登録
efkRegMemFile fileName, fileBin, fileBinSize

// バイナリリソースの削除
efkUnregMemFile fileName


一回一回メモリに読み込んでおくのと,予め読み込んで登録しておく必要があるので扱いづらいところですが,HSP上で使用する以上はこれぐらいの使い方にしておかないと結構難しいという都合です.(HSPでコールバック処理するのは,stopとかがあるので気が引ける)
基本的にメモリ富豪的な使い方を想定していますが,メモリもなるべく使いたくないという用途であれば,エフェクトロード時にだけバイナリリソースを登録しておき,そうでない時は全てバイナリリソースを削除して解放するしか現状ないです.

●後半:Effekseer組み込みプラグインの立ち位置,内部で動作する補完的な処理について

プラグインの立ち位置と,中でEffekseerランタイムラップ処理以外にどういった処理をしているかについて.

プラグインの立ち位置

そもそもこの記事が「DXライブラリ+Effekseer」と言っているし,DXライブラリはそのままDLLとして持ってきているので気付いている方も多いと思いますが,プラグインとしてはEffekseerランタイムのことをマネージしますし,むしろそれ以外については1ミリもマネージしません,管轄外です.

ただ,Effekseerが描画の役割も担っていることと,Effekseerの歪みエフェクトなど現在の描画ターゲットに関連する情報を引き出そうとするため,プラグインとしてちょっと入り組んだ処理をしている箇所があったりします.
とまぁ,そういった役割を図示するとこんな感じになります.
effekt_plugin_relationships.jpg

重要なのは,描画エンジンしてるDXライブラリとEffekseerはモジュールという意味で同列だということです.
DirectXのデバイスハンドルを作って色々使っているので所有者はDXライブラリですが,Effekseer側もデバイスハンドルを教えてもらって描画を行います.

と,立ち位置を説明したので,後はプラグイン側で一体何をしているかについて.

歪み背景の自動設定

Effekseerの比較的新しい機能で「歪み」というのがありますが,これがランタイム実装的にはちょっとトリッキーで,APIの的には「歪みの背景となるテクスチャを設定する」項目しかありません.
背景が動かないならまだしも,3Dのようにカメラがゴリゴリ動いて背景が変わっていく中で全面に歪みエフェクトを出そうとするとちょっと苦労する.

そこで,プラグイン側で歪みエフェクト再生時のコールバックを受けて,その場でレンダリングターゲットの内容をテクスチャにコピー,「歪みの背景となるテクスチャ」として設定してから歪み処理へと繋げています.
実はこの処理フロー,Effekseerのビューアツールの中の処理と同一で,予め作っておいたレンダリングバッファと同じテクスチャにコピーしてから差し替えてます.
// @@EffekseerTool.Renderer.DistortingCallback
void Renderer::DistortingCallback::OnDistorting()
{
auto texture = renderer->ExportBackground();
renderer->m_renderer->SetBackground(texture);
}

この処理とても汎用的なのでEffekseerの中で組み込み用途としてありそうだったんですが,どうやらAPIとして公開されていない,というよりビューアツール特化実装の様子.

…あれぇ~…….

ということで,プラグイン側でも同様の処理となるように独自に実装されています.
また,歪みエフェクトを使う際に必要な「efkInitDistortion」命令は,正にレンダリングターゲットをコピーするテクスチャを生成する処理にあたります.
「efkInitDistortion」で描画領域の大きさを渡すのはそういう理由でした.

なお一方,この実装にしたお陰で歪みに使うテクスチャのサイズがレンダリングターゲットとは無関係になり,歪みに映るところだけ低解像度みたいなよく分からないエフェクトも作れるようになった,という副次産物もあるにはあります.

レンダリングターゲットコピー実装の話

実装の話.
私みたいにDirectX9とか門外漢の人は結構この程度の実装でも重かったりするので,同じような人が居るんだったら参考にでもなれば是幸い.

コピー用テクスチャは予め作っておくところ(実際はクラスです).
    // member variable
LPDIRECT3DDEVICE9 m_d3d_device;

int m_width, m_height;

IDirect3DSurface9* m_renderEffectBackTarget = nullptr;
IDirect3DTexture9* m_renderEffectBackTargetTexture = nullptr;

// member function : initialize texture
bool GenerateRenderTargets(int32_t width, int32_t height)
{
m_width = 0;
m_height = 0;

// Effekseerのヘッダincludeしてると使える便利系マクロ
ES_SAFE_RELEASE(m_renderEffectBackTarget);
ES_SAFE_RELEASE(m_renderEffectBackTargetTexture);

if (width == 0 || height == 0)
{ return false; }

HRESULT hr;

hr = m_d3d_device->CreateTexture(
width,
height,
1,
D3DUSAGE_RENDERTARGET,
D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT,
&m_renderEffectBackTargetTexture,
NULL
);
if (FAILED(hr))
{
return false;
}

// mip level0のサーフェースをとっておく
m_renderEffectBackTargetTexture->GetSurfaceLevel(0, &m_renderEffectBackTarget);

m_width = width;
m_height = height;
return true;
}


レンダリングターゲットからサーフェースコピってくるところ.
IDirect3DTexture9* ExportBackground()
{
IDirect3DSurface9* tempRender = nullptr;
IDirect3DSurface9* tempDepth = nullptr;

// get current target
m_d3d_device->GetRenderTarget(0, &tempRender);
m_d3d_device->GetDepthStencilSurface(&tempDepth);

// replace targets for copy
m_d3d_device->SetRenderTarget(0, m_renderEffectBackTarget);
m_d3d_device->SetDepthStencilSurface(nullptr);

D3DSURFACE_DESC desc;
tempRender->GetDesc( &desc );
const bool isSame = ( desc.Width==m_width && desc.Height==m_height );

// copy from front buffer
m_d3d_device->StretchRect(
tempRender,
nullptr,
m_renderEffectBackTarget,
nullptr,
(isSame ? D3DTEXF_POINT : D3DTEXF_LINEAR)
);

// restore targets
m_d3d_device->SetRenderTarget(0, tempRender);
m_d3d_device->SetDepthStencilSurface(tempDepth);

// discacrd temporary
ES_SAFE_RELEASE(tempRender);
ES_SAFE_RELEASE(tempDepth);

return m_renderEffectBackTargetTexture;
}

サーフェースからサーフェースへコピるのはStrechRect使うと極めてシンプルに書ける.
サーフェース情報とってきてサイズ比較してサンプラの設定変えてるけど,LINEARよりPOINTのが早い保障ってどっかにあったっけか,うろ覚え.

OnDistortionコールバック
void OnDistorting() override
{
auto texture = ExportBackground();
renderer_->SetBackground(texture);
}

見た感じの通りビューアツールと同じコードであんまりやる気ないです.

バイナリリソースの取り扱い

バイナリリソースとして登録されたものは,ファイルパスで一致している限り次の3つのリソース何れとしても扱われ得ます.

・エフェクトファイル(.efkとか)
・テクスチャファイル(.pngとか)
・モデルファイル(.efkmodelとか)

内部実装的には,EffectLoaderとTextureLoaderとModelLoaderのFileInterfaceに独自のものを差し込んでいます.
 (このうち,EffectLoaderだけはどうやらDefaultEffectLoaderがクライアントからは触れないところに居るようで,やっぱり移植したという経緯があったりしますが)

FileInterfaceの実装内容は単純に連想配列相当なので説明の必要はないですね.

サーバー機能との連動

処理というほど大層なことではないですが,サーバー機能を有効化した後は次の処理が各命令に追加されます.

・「efkLoad」時,ファイルパスのディレクトリ部分と拡張子を削った文字列を「::Effekseer::Server」にRegist
・「efkUnload」時にUnregist
・「efkUpdate」時にサーバーのUpdateもついでに実行

説明してないですが,サーバー機能を途中で破棄したいときは「efkDestroyServer」で出来たりします.

プラグイン作成時にEffekseerで追加でラップした処理は以上のような感じです.
歪み機能とかランタイム組み込み側では現状ではちょっと罠に近いので,躓いている人の助けにでもなるのだったら幸い.

終わりに


今回はHSP+DXライブラリ+Effekseerということで,敢えて自分から針の筵に座りにいくスタイルのプラグインの公開及び使い方解説でした.

Effekseer使ってみて思ったんですが,これだけ使いやすいにも関わらずそこそこスプライトを出しても処理負荷がそんなに上がらなかったところを見ると,良いライブラリだなーと思いました.
当然描画速度は画面フィルにもよるでしょうし,CPU側の処理にしたって各パーティクルの更新処理はC++側のコードなので,HSPと比較したらそりゃあ余裕だと感じるだけだろう,というバイアスがかかっているのかもしれませんが….

本記事後半パートを覗いた人は気づいたかもしれませんが,実装的にはDX9のデバイスハンドルが取得できるのであればどのような描画エンジンとも共存可能です,レンダーステートの取り回しがかなり煩雑にはなりますが.
hgimg4とかもDX9版があったはずなので,ネイティブハンドルをAPI経由からでも出してくれればこのプラグイン使えるんじゃないかな,という希望的観測.
マルチプラットフォームを謳うhgimg4が,それをしてくれる気はあんまりしてませんが….

それでは今回はこの辺で.
関連記事
スポンサーサイト

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://fe0km.blog.fc2.com/tb.php/120-6e9e88fd
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。