2012年

10月

29日

DirectXを子ウィンドウで使いたいだけ

今回は子ウィンドウだけをDirectXで描画する方法を紹介します。

私はSimDXを使ったソフトウェア開発をしてるのですが、今まで単一ウィンドウをDirectXで操作するアプリケーションしか作ってこなかったので今回のプログラムの実装に随分と悩まされました。

 

DirectXで子ウィンドウ描画は出来るものの、メインループの処理を DirectX側でやる方法が分からず1年位ずっと悩み、先日ようやく解決することが出来ました。

 

とある勘違いをずっとしてまして・・・

SlimDXにはWindowを簡単に作成&操作できる補助ライブラリが備わっていて、その中にメッセージループを呼び出す静的関数があります。

 


メッセージループを開始する(他オーバーライド関数が2つ)

public static void SlimDX.Windows.MessagePump.Run(

                System.Windows.Forms.Form form,
                SlimDX.Windows.MainLoop mainLoop

)

 

form:表示したいフォームウィンドウ

mainLoop:メインループ処理イベント


 

この関数を呼び出すとループ処理が始まります。C++の頃と比べるととても便利です。

 

で、私はこの第一引数にはSlimDX.Windows.RenderFormのインスタンスしか渡せないものだとこの1年間ずっと勘違いしていました・・・上記を見てもらえば分かるとおり、そんなことはありません。System.Windows.Forms.Formクラスを渡しても大丈夫なんです。

 

使い方はとても簡単で、Formアプリケーションを動かすのに呼び出すSystem.Windows.Forms.Application.RunをコチラのMessagePump.Runに変えるだけです。

 

以下は使用例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Program.cs
#define USE_DIRECTX // DirectXが使用されている

using System;
using System.Windows.Forms;

namespace DirectXSample
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Form1 mainForm = null;
            Application.EnableVisualStyles();            
            Application.SetCompatibleTextRenderingDefault(false);
            
            try
            {
                // Mainウィンドウ
                mainForm = new Form1();

#if USE_DIRECTX // DirectXを使う場合
                SlimDX.Windows.MessagePump.Run(mainForm, mainForm.MainLoop);

#else // アプリケーションクラスを使う場合
                Application.Run(mainForm);

#endif
            }
            catch (Exception ex)
            {
                // エラーメッセージ
                MessageBox.Show(
                    ex.Message,
                    "エラー",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
            finally
            {
                // Formの開放
                if (mainForm != null)
                {
                    if (!mainForm.IsDisposed)
                        mainForm.Dispose();

                    mainForm = null;
                }
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Windows.Forms;

namespace DirectXSample
{
    public partial class Form1 : Form
    {
        DirectXWindow child;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Form1()
        {
            // デザイナー設定
            InitializeComponent();

            // DirectXウィンドウの追加
            child = new DirectXWindow(this);
        }

        /// <summary>
        /// 表示開始
        /// </summary>
        /// <param name="e"></param>
        protected override void OnShown(EventArgs e)
        {
            // DirectX子ウィンドウの表示を開始
            if (child != null && !child.IsDisposed)
                child.Show();

            base.OnShown(e);
        }

        /// <summary>
        /// リサイズ
        /// </summary>
        /// <param name="e"></param>
        protected override void OnResize(EventArgs e)
        {
            // 子ウィンドウのリサイズ
            if (child != null && child.Visible)
            {
                // 親の描画領域ぴったりに合わせる
                child.Width = this.ClientSize.Width;
                child.Height = this.ClientSize.Height;
            }

            base.OnResize(e);
        }

        /// <summary>
        /// 更新メソッド
        /// </summary>
        public void MainLoop()
        {
            // DirectXウィンドウ更新
            if (child != null && child.Visible)
                child.Idle();
        }
    }
}

DirectXWindow.csは長くなるので省略。

プログラムの実行結果はこのようになります。

 

子ウィンドウのタイトルバーが必要ないなら、FormBorderStyleをFormBorderStyle.Noneにすれば消すことが出来ます。

 

ソースファイルに興味のある方は以下からダウンロードしてください

(DirectXWindow.csも入ってます)

ソースファイル
DirectXSample_source.rar
圧縮ファイル アーカイブ 6.4 KB
バイナリファイル
SlimDXライブラリ同梱。

必要な環境
.NET Framework4 Client Profile
SlimDX Runtime
C++ Runtime
DirectXSample_binary.rar
圧縮ファイル アーカイブ 3.6 MB

おまけ。

 

以前までのやり方も書いてみました。以下の方法でもループ可能なのですが、Timer.Intervalの数値どおりのFPSにならないので使いづらいです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using SlimDX.Direct3D9;

namespace DirectXChidView
{
    public abstract class DirectXControl : Control
    {
        /// <summary>
        /// グラフィックデバイス
        /// </summary>
        public Device GraphicsDevice { get; set; }

        /// <summary>
        /// タイマー
        /// </summary>
        /// <summary>
        Timer timer;

        /// <summary>
        /// コントロール作成
        /// </summary>
        protected override void OnCreateControl()
        {
            base.OnCreateControl();

            // ループ用タイマー作成
            timer = new Timer();
            timer.Tick += new EventHandler(MainLoop); // イベント
            timer.Interval = 1000 / 60; // 周期時間(1秒間に60回呼びたい)
            timer.Start();
        }

        /// <summary>
        /// メインループ
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void MainLoop(object sender, EventArgs e)
        {
            // 描画イベント(WM_PAINT)を発生
            Invalidate();
        }

        /// <summary>
        /// ペイント
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            // 描画範囲の設定
            Viewport viewport = GraphicsDevice.Viewport;
            viewport.Width = ClientSize.Width;
            viewport.Height = ClientSize.Height;
            GraphicsDevice.Viewport = viewport;

            // 描画開始
            GraphicsDevice.BeginScene();

            // 画面クリア
            GraphicsDevice.Clear(
                    ClearFlags.Target | ClearFlags.ZBuffer,
                    BackColor,
                    1.0f, 0);
            
            /*
             * フレームの更新&描画
             * 省略
            */

            // 描画終了
            GraphicsDevice.EndScene();
            GraphicsDevice.Present(ClientRectangle, ClientRectangle, Handle);
        }
        
        // 以下省略
    }
}