フレーム階層:RMBegin2サンプル

デバイスの作成、シーンの構築と移動は、Microsoft® Direct3D®保持モードの基本的な概念である。RMBegin2サンプルは、RMBegin1サンプルで紹介した、この基本概念の上に構築されている。この節では、RMBegin2サンプルのコードのウォークスルーを行う。

RMBegin2サンプルは、RMBegin1と同様、デフォルトデバイスを作成し、Microsoft DirectX®ファイルからメッシュをロードし、オブジェクトに色を追加し、シーンに照明を追加し、オブジェクトを回転する。RMBegin2サンプルでは、RMBegin1よりも複雑なシーンを構築し、オブジェクトをスケーリングし、フレーム階層を使用する。シーンを構築する際、このサンプルでは、3つのメッシュビルダ、複数のフレーム、複数の照明を使用する。また、DirectDrawClipperオブジェクト、ビューポート、カメラも作成する。RMBegin2サンプルには、アプリケーションのウィンドウサイズを変更する際シーンをリサイズし、WM_ACTIVATEメッセージとWM_PAINTメッセージを処理する新しい機能が追加されている。

RMBegin2では、3つの球体 (太陽、地球、月) のフレーム階層を使用する。各球体はそれぞれの親フレームのまわりを回転する。

RMBegin2サンプルのコードは、必要な要素をすべて備えた1つのファイル (RMBegin2.cpp) で構成されている。分かりやすくするため、Microsoft Windows® DirectDraw®およびDirect3D保持モードの標準ヘッダファイル以外の個別のヘッダファイルは使用していない。メニューやアイコンなどのリソースも使用していない。

このサンプルは、DirectX Foundation SDK (Software Development Kit) のSphere0.x、Sphere1.x、Sphere2.xファイルを使用して、シーンに月、地球、太陽の画像を与える。デフォルトでは、SDKをインストールすると、SDKのセットアッププログラムは、環境変数をSDKのメディアディレクトリに設定する。3つのDirectX (.x) ファイルはメディア・ディレクトリにあるので、アプリケーションはファイルを自動的に検出する。SDKをインストールしないでRMBegin2.exeを実行する場合、DirectXファイルはRMBegin2.exeと同じディレクトリに置く必要がある。

この項は、ユーザがCまたはC++のプログラミング経験があること、およびメッセージループ、ウィンドウの作成、メニューの使用など、Windowsプログラミングの基本概念に精通していることを前提にしている。

この項は、次の項目に分かれている。

前提

グローバル宣言

このサンプルでは、いくつかの定数とグローバル変数を定義して、情報を整理する。

アプリケーションで定義されているRELEASEマクロは、オブジェクトがNULL以外の場合にだけオブジェクトを解放し、解放後にオブジェクトをNULLに初期化する。すでに解放したオブジェクトを解放すると、ページフォールトなどの予期せぬ結果が発生するが、この手順によって、そうした問題が回避できる。

// オブジェクトを解放するマクロ
#define RELEASE(x) if (x != NULL) {x->Release(); x = NULL;}

アプリケーションで定義されているDISPLAYMSGマクロは、Windows MessageBox関数を使用してユーザに対して情報を表示する。このマクロを使用すると、サンプルコードでMessageBoxを直接呼び出すよりも、コードが簡素化されて読みやすくなる。

// 与えられた文字列を含むメッセージボックスを表示するマクロ
#define DISPLAYMSG(x) MessageBox(NULL, x, "D3DRM Sample", MB_OK);

グローバル変数は、Direct3D保持モードおよびDirectDrawClipperオブジェクトなどの主要なオブジェクトを追跡する。DirectDrawClipperオブジェクトは、描画を指定した領域に制限する。myglobs構造体は、デバイス、ウィンドウデバイス、ビューポート、シーン、カメラに関する情報に加えて、アプリケーションの最小化やオブジェクトの初期化に関する情報も収集する。RMBegin2では、RMBegin1で使用していたオブジェクトにDirect3D保持モードのウィンドウデバイスを追加していることに注意する。

// グローバル変数
LPDIRECT3DRM lpD3DRM = NULL;            // Direct3D保持モードブジェクト
LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // DirectDrawClipperオブジェクト

// グローバル構造体
struct _myglobs {

    // Direct3D保持モードデバイス
    LPDIRECT3DRMDEVICE dev;

    // Direct3D保持モードウィンドウデバイス
    LPDIRECT3DRMWINDEVICE windevice;

    // シーンを表示するDirect3D保持モードビューポート
    LPDIRECT3DRMVIEWPORT view;

    // 他のフレームを配置するマスターフレーム
    LPDIRECT3DRMFRAME scene;

    // ユーザの視点を記述するフレーム
    LPDIRECT3DRMFRAME camera;

    // アプリケーションは最小化されている。
    BOOL bMinimized;

    // Direct3D保持モードの全オブジェクトが初期化されている。
    BOOL bInitialized;

} myglobs;

関数概要

RMBegin2.cppには、以下の関数が含まれている。

BuildScene関数は、レンダリングするシーンを作成する。DirectXファイルからメッシュをロードし、メッシュをスケーリングし、色をメッシュに追加し、フレームの階層を作成し、シーンに照明を追加する。さらに、BuildSceneは、オブジェクトの位置と回転の方法を設定する。グローバル変数を初期化し、デフォルトのデバイス、フレーム、ビューポートを作成するCreateObjectsが、この関数を使用する。基本的には、RMBegin1のBuildScene関数に似ているが、RMBegin2のBuildScene関数は、より複雑なシーンを作成する。

BOOL BuildScene(LPDIRECT3DRM, LPDIRECT3DRMDEVICE,
                LPDIRECT3DRMFRAME, LPDIRECT3DRMFRAME);

RenderScene関数は、BuildSceneが作成したシーンを描画する。WinMainがこの関数を使用するたびに、RenderSceneはシーンを移動して、オブジェクトに回転効果を与える。この関数は、RMBegin1サンプルのRenderScene関数と同じである。

static BOOL RenderScene(void);

CreateObjects関数は、グローバル変数を初期化して、デフォルトのデバイス、ウィンドウデバイス、カメラフレーム、シーンフレーム、ビューポートを作成する。CreateObjectsBuildSceneを使用して、レンダリングするシーンを作成する。アプリケーションのウィンドウを作成するInitAppが、CreateObjectsを使用する。この関数は、ウィンドウデバイスを作成する点を除けば、RMBegin1サンプルのCreateObjects関数と同じである。

BOOL CreateObjects(HWND win);

InitApp関数は、Windowsアプリケーションの標準的なコードにより、ウィンドウクラスとアプリケーションのウィンドウを作成する。InitAppは、CreateObjectsを使用して、グローバルオブジェクトを初期化する。この関数は、ウィンドウキャプションが異なることを除けば、RMBegin1サンプルのInitApp関数と同じである。

static HWND InitApp(HINSTANCE, int);

WinMain関数は、アプリケーションのメッセージループを提供する。InitAppを使用してアプリケーションを初期化し、RenderSceneを使用してシーンを描画する。この関数は、RMBegin1サンプルのWinMain関数と同じである。

int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

WindowProc関数は、アプリケーションのメッセージを処理する。アプリケーションの最小化、サイズ変更、アクティブ化、描画、破棄を処理する。

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

以上の関数の詳細については、この項の次の項目を参照すること。

BuildScene

BuildScene関数は、レンダリングするシーンを作成する。Direct3D保持モードのすべてのアプリケーションはシーンを作成する必要があるが、必要なコードはシーンに依存する。アプリケーションは通常、フレームと照明を作成し、視覚的な要素をシーンに追加し、オブジェクトの位置を設定する。この関数は、さらに、オブジェクトをスケーリングし、フレーム階層を設定する。

この節では、BuildScene関数のコードを部分的に示して、RMBegin2サンプルが太陽、地球、月のシーンを構築する手順について説明する。さらに、RMBegin2サンプルのBuildScene関数に関するすべてのソースコードも示す。エラーが発生した場合は、goto文で関数の最後にジャンプして、関数から返る前に、すべてのオブジェクトを適切に解放する。

  1. IDirect3DRM3::CreateMeshBuilderIDirect3DRMMeshBuilder3::Loadメソッドを使用して、DirectXファイルからメッシュをロードする。次のコードは、1つのメッシュをロードしている。サンプルでは、同じ処理を使用して、他のメッシュもロードしている。
        if (FAILED(lpD3DRM->CreateMeshBuilder(&sun_builder)))
            goto generic_error;
    
        rval = sun_builder->Load("sphere2.x", NULL,
                        D3DRMLOAD_FROMFILE, NULL, NULL);
        if (FAILED(rval)) {
            DISPLAYMSG("Failed to load sun .x file.");
            goto ret_with_error;
        }
    
  2. メッシュをスケーリングして色を追加する。サンプルがロードする3つの球体は、ほぼ同じ大きさなので、太陽を拡大し、地球と月を縮小する。次のコードは、1つのメッシュに対して、この処理を行っている。
        if (FAILED(sun_builder->Scale(D3DVAL(1.2), D3DVAL(1.2),
                                      D3DVAL(1.2))))
            goto generic_error;
        if (FAILED(sun_builder->SetColorRGB(D3DVAL(1.0), D3DVAL(1.0),
                                            D3DVAL(0.0))))
            goto generic_error;
    
  3. IDirect3DRM3::CreateFrameメソッドを使用して、シーンのフレーム階層を作成する。次のコードでは、マスターシーンフレームに太陽のフレーム、太陽のフレームに地球のフレーム、地球のフレームに月のフレームを作成している。オブジェクトの回転を設定するとき、このフレーム階層は、どのオブジェクトの回りを、どのオブジェクトが公転するかを管理する。
        if (FAILED(lpD3DRM->CreateFrame(scene, &sun)))
            goto generic_error;
        if (FAILED(lpD3DRM->CreateFrame(sun, &planet)))
            goto generic_error;
        if (FAILED(lpD3DRM->CreateFrame(planet, &moon)))
            goto generic_error;
    
  4. IDirect3DRMMeshBuilder3::CreateMeshを使用して、メッシュビルダオブジェクトをメッシュに変換する。次のコードは太陽のメッシュを作成する。
        if (FAILED(sun_builder->CreateMesh(&sun_mesh)))
            goto generic_error;
    
  5. IDirect3DRMFrame3::AddVisualメソッドを使用して、ロードしたメッシュを適切な子フレームに追加する。次のコードは、太陽のメッシュを太陽のフレームに追加する。
        if (FAILED(sun->AddVisual((LPDIRECT3DRMVISUAL) sun_mesh)))
            goto generic_error;
    
  6. IDirect3DRMFrame3::SetPositionメソッドを使用して、シーンにカメラ位置を設定する。他のオブジェクトの位置も設定する。次のコードはカメラと太陽の位置を設定する。太陽の位置とカメラ位置のx座標とy座標が、ともに(0,0)であるため、太陽はカメラの正面の直線上に表示される。
        if (FAILED(camera->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                       -D3DVAL(25))))
            goto generic_error;
        if (FAILED(sun->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                    D3DVAL(0))))
            goto generic_error;
    
  7. IDirect3DRMFrame3::SetRotationメソッドを使用して、フレームの回転を設定する。次のコードは、太陽の回転を設定する。
        if (FAILED(sun->SetRotation(scene, D3DVAL(0), D3DVAL(0),
                                    D3DVAL(1),
                                    D3DVAL(0.01))))
    
  8. IDirect3DRM3::CreateFrameメソッドを使用して、シーンの子フレームになる照明フレームを作成する。IDirect3DRMFrame3::SetPositionメソッドを使用して、シーンに照明フレームを配置する。IDirect3DRM3::CreateLightRGBIDirect3DRMFrame3::AddLightメソッドを使用して、明るいポイント照明を作成し、照明フレームに追加する。
        if (FAILED(lpD3DRM->CreateFrame(scene, &lights)))
            goto generic_error;
        if (FAILED(lights->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                    -D3DVAL(7))))
            goto generic_error;
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(0.9),
                                      D3DVAL(0.8), D3DVAL(0.7), &light1)))
            goto generic_error;
        if (FAILED(lights->AddLight(light1)))
            goto generic_error;
    
  9. IDirect3DRM3::CreateLightRGBIDirect3DRMFrame3::AddLightメソッドをもう一度使用して、暗いアンビエント照明を作成し、シーンに追加する。
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1),
                                      D3DVAL(0.1), D3DVAL(0.1), &light2)))
            goto generic_error;
        if (FAILED(scene->AddLight(light2)))
            goto generic_error;
    
  10. アンビエント照明の作成と同じメソッドを使用して、明るいパラレルポイント照明を作成し、照明フレームに追加する。
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT,
                                      D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9),
                                      &light3)))
            goto generic_error;
        if (FAILED(sun->AddLight(light3)))
            goto generic_error;
    
  11. この関数で作成したすべてのオブジェクトを解放して終了する。
        RELEASE(sun_builder);
        RELEASE(planet_builder);
        RELEASE(moon_builder);
        RELEASE(sun_mesh);
        RELEASE(planet_mesh);
        RELEASE(moon_mesh);
        RELEASE(sun);
        RELEASE(planet);
        RELEASE(moon);
        RELEASE(lights);
        RELEASE(light1);
        RELEASE(light2);
        RELEASE(light3);
    

BuildScene関数ソースコード

// レンダリングするシーンを作成する。

BOOL BuildScene(LPDIRECT3DRM lpD3DRM,
                LPDIRECT3DRMDEVICE dev,
                LPDIRECT3DRMFRAME scene,
                LPDIRECT3DRMFRAME camera)
{
    LPDIRECT3DRMMESHBUILDER sun_builder = NULL;
    LPDIRECT3DRMMESHBUILDER planet_builder = NULL;
    LPDIRECT3DRMMESHBUILDER moon_builder = NULL;
    LPDIRECT3DRMMESH sun_mesh = NULL;
    LPDIRECT3DRMMESH planet_mesh = NULL;
    LPDIRECT3DRMMESH moon_mesh = NULL;
    LPDIRECT3DRMFRAME sun = NULL;
    LPDIRECT3DRMFRAME planet = NULL;
    LPDIRECT3DRMFRAME moon = NULL;
    LPDIRECT3DRMLIGHT light1 = NULL;
    LPDIRECT3DRMLIGHT light2 = NULL;
    LPDIRECT3DRMLIGHT light3 = NULL;
    LPDIRECT3DRMFRAME lights = NULL;
    HRESULT rval;


    // ロードするDirectXファイル (太陽、地球、月) ごとに、メッシュビルダを
    // 作成して、メッシュをロードする。
    if (FAILED(lpD3DRM->CreateMeshBuilder(&sun_builder)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateMeshBuilder(&planet_builder)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateMeshBuilder(&moon_builder)))
        goto generic_error;

    rval = sun_builder->Load("sphere2.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load sun .x file.");
        goto ret_with_error;
    }

    rval = planet_builder->Load("sphere1.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load planet .x file.");
    goto ret_with_error;
    }

    rval = moon_builder->Load("sphere0.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load moon .x file.");
        goto ret_with_error;
    }

    // 太陽、地球、月のメッシュのサイズを調整する。
    // x 軸、y 軸、z 軸に同じ大きさでスケーリングして、メッシュの球体を保つ。
    // 太陽のサイズは増やし、
    // 地球と月のサイズは減らして、
    // 適切な相対的な大きさを得る。
    if (FAILED(sun_builder->Scale(D3DVAL(1.2), D3DVAL(1.2),
                                  D3DVAL(1.2))))
        goto generic_error;
    if (FAILED(planet_builder->Scale(D3DVAL(0.5), D3DVAL(0.5),
                                     D3DVAL(0.5))))
        goto generic_error;
    if (FAILED(moon_builder->Scale(D3DVAL(0.2), D3DVAL(0.2),
                                   D3DVAL(0.2))))
        goto generic_error;

    // 太陽、地球、月のメッシュの色を調整する。
    // 太陽は黄色、地球は緑がかった青色、
    // 月は赤色に設定する。
    if (FAILED(sun_builder->SetColorRGB(D3DVAL(1.0), D3DVAL(1.0),
                                        D3DVAL(0.0))))
        goto generic_error;
    if (FAILED(planet_builder->SetColorRGB(D3DVAL(0.2), D3DVAL(1.0),
                                           D3DVAL(0.8))))
        goto generic_error;
    if (FAILED(moon_builder->SetColorRGB(D3DVAL(0.7), D3DVAL(0.2),
                                         D3DVAL(0.3))))
        goto generic_error;

    // フレームの階層を作成する。
    // - マスターシーンフレームに太陽のフレーム、
    // - 太陽のフレームに地球のフレーム、
    // - 地球のフレームに月のフレームを作成する。
    if (FAILED(lpD3DRM->CreateFrame(scene, &sun)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateFrame(sun, &planet)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateFrame(planet, &moon)))
        goto generic_error;

    // メッシュビルダからメッシュを作成して、Direct3D保持モードが同じ変換を
    // 繰り返すのを避ける。メッシュビルダはここで解放することもできるが、
    // コードを分かりやすくするために、この関数の最後で、
    // 他のオブジェクトと一緒に解放する。
    if (FAILED(sun_builder->CreateMesh(&sun_mesh)))
        goto generic_error;
    if (FAILED(planet_builder->CreateMesh(&planet_mesh)))
        goto generic_error;
    if (FAILED(moon_builder->CreateMesh(&moon_mesh)))
        goto generic_error;

    // メッシュをフレームに追加する。
    if (FAILED(sun->AddVisual((LPDIRECT3DRMVISUAL) sun_mesh)))
        goto generic_error;
    if (FAILED(planet->AddVisual((LPDIRECT3DRMVISUAL) planet_mesh)))
        goto generic_error;
    if (FAILED(moon->AddVisual((LPDIRECT3DRMVISUAL) moon_mesh)))
        goto generic_error;

    // フレームの位置を設定する。
    if (FAILED(camera->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                   -D3DVAL(25))))
        goto generic_error;
    if (FAILED(sun->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                D3DVAL(0))))
        goto generic_error;
    if (FAILED(planet->SetPosition(sun, D3DVAL(7), D3DVAL(0),
                                   D3DVAL(0))))
        goto generic_error;
    if (FAILED(moon->SetPosition(planet, D3DVAL(0), D3DVAL(3),
                                 D3DVAL(0))))
        goto generic_error;

    // フレームの回転を設定する。
    if (FAILED(sun->SetRotation(scene, D3DVAL(0), D3DVAL(0),
                                D3DVAL(1),
                                D3DVAL(0.01))))
        goto generic_error;
    if (FAILED(planet->SetRotation(sun, D3DVAL(1), D3DVAL(0),
                                   D3DVAL(0),
                                   D3DVAL(0.02))))
        goto generic_error;
    if (FAILED(moon->SetRotation(planet, D3DVAL(0.1), D3DVAL(0.2),
                               D3DVAL(0.7),
                               D3DVAL(0.03))))
       goto generic_error;

    // シーンの照明を初期化する。
    // - 照明フレームを作成し、シーンに配置する位置を設定する
    //   (太陽の正面の直線上に置き、太陽を照らす)。
    //   ポイント照明を照明フレームにアタッチする。
    // - ソフトなアンビエント照明を作成して、シーンに追加する。
    // - 明るいパラレルポイント照明を作成して、太陽のフレームに
    //   追加する。地球と月が太陽の回りを公転する際、
    //   適切に照らすようにする。

    // 太陽の正面にポイント照明を作成する。
    if (FAILED(lpD3DRM->CreateFrame(scene, &lights)))
        goto generic_error;
    if (FAILED(lights->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                -D3DVAL(7))))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(0.9),
                                  D3DVAL(0.8), D3DVAL(0.7), &light1)))
        goto generic_error;
    if (FAILED(lights->AddLight(light1)))
        goto generic_error;

    // アンビエント照明を作成し、シーンに追加する。
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1),
                                  D3DVAL(0.1), D3DVAL(0.1), &light2)))
        goto generic_error;
    if (FAILED(scene->AddLight(light2)))
        goto generic_error;

    // パラレルポイント照明を作成し、太陽のフレームに追加する。
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT,
                                  D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9),
                                  &light3)))
        goto generic_error;
    if (FAILED(sun->AddLight(light3)))
        goto generic_error;

    // 終了処理
    RELEASE(sun_builder);
    RELEASE(planet_builder);
    RELEASE(moon_builder);
    RELEASE(sun_mesh);
    RELEASE(planet_mesh);
    RELEASE(moon_mesh);
    RELEASE(sun);
    RELEASE(planet);
    RELEASE(moon);
    RELEASE(lights);
    RELEASE(light1);
    RELEASE(light2);
    RELEASE(light3);
    return TRUE;

generic_error:
    DISPLAYMSG("A failure occurred while building the scene.");
ret_with_error:
    RELEASE(sun_builder);
    RELEASE(planet_builder);
    RELEASE(moon_builder);
    RELEASE(sun_mesh);
    RELEASE(planet_mesh);
    RELEASE(moon_mesh);
    RELEASE(sun);
    RELEASE(planet);
    RELEASE(moon);
    RELEASE(lights);
    RELEASE(light1);
    RELEASE(light2);
    RELEASE(light3);
    return FALSE;
}

RenderScene

RenderScene関数は、実際にシーンをスクリーンに描画する。BuildSceneが作成したシーンを次の手順に従って描画する。

  1. IDirect3DRMFrame3::Moveを使用して、シーンを移動する。
  2. IDirect3DRMViewport2::Clearを使用して、ビューポートをクリアする。
  3. IDirect3DRMViewport2::Renderを使用して、シーンをレンダリングする。
  4. IDirect3DRMDevice3::Updateを使用して、ウィンドウを更新する。

RenderScene関数ソースコード

// ビューポートをクリアし、次のフレームをレンダリングし、ウィンドウを更新する。

static BOOL RenderScene()
{
    HRESULT rval;

    // シーンの移動。
    rval = myglobs.scene->Move(D3DVAL(1.0));
    if (FAILED(rval))
    {
        DISPLAYMSG("Moving scene failed.");
        return FALSE;
    }

    // ビューポートのクリア。
    rval = myglobs.view->Clear();
    if (FAILED(rval))
    {
        DISPLAYMSG("Clearing viewport failed.");
        return FALSE;
    }

    // シーンをビューポートにレンダリングする。
    rval = myglobs.view->Render(myglobs.scene);
    if (FAILED(rval))
    {
        DISPLAYMSG("Rendering scene failed.");
        return FALSE;
    }

    // ウィンドウの更新。
    rval = myglobs.dev->Update();
    if (FAILED(rval))
    {
        DISPLAYMSG("Updating device failed.");
        return FALSE;
    }
    return TRUE;
}

CreateObjects

CreateObjectsは、サンプルのグローバル変数を初期化し、オブジェクトを作成する。グローバル変数を初期化した後、デバイスの作成に必要な手順を実行し、デバイスをアプリケーションのメインウィンドウに関連付け、マスターシーンフレーム、カメラフレーム、ビューポートを作成する。以下の手順に従う。

  1. DirectDrawのDirectDrawCreateClipper関数を使用して、DirectDrawClipperオブジェクトを作成する。
  2. DirectDrawClipperオブジェクトのSetHWndメソッドを使用して、アプリケーションのウィンドウをDirectDrawClipperに関連付ける。DirectDrawClipperオブジェクトは、後続の描画を、指定した領域 (ここではアプリケーションのウィンドウ) に制限する。
  3. Direct3DRMCreate関数を使用して、Direct3D保持モードオブジェクトを作成する。
  4. WindowsのGetClientRect関数を使用して、アプリケーションのメインウィンドウの高さと幅を取得する。
  5. Direct3D保持モードオブジェクトのCreateDeviceFromClipperメソッドを使用してデバイスを作成し、NULLを渡してデフォルトデバイスを作成する。さらに、アプリケーションのウィンドウの幅と高さをメソッドに渡す。
  6. IDirect3DRMWinDeviceインターフェイスをサポートするデバイスを問い合わせて、Direct3D保持モードのウィンドウデバイスを作成する。ウィンドウデバイスは、WM_ACTIVATEメッセージとWM_PAINTメッセージを処理する。
  7. IDirect3DRM3::CreateFrameを使用して、マスターシーンフレームを作成する。
  8. IDirect3DRM3::CreateFrameを使用して、カメラフレームを作成する。
  9. IDirect3DRM3::CreateViewportを使用して、Direct3D保持モードのビューポートを作成する。
  10. BuildSceneを使用して、シーンを作成する。

CreateObjects関数ソースコード

// グローバル変数を初期化し、デバイスとオブジェクトを作成する。

BOOL CreateObjects(HWND win)
{
    HRESULT rval; // 戻り値
    RECT rc;      // メインウィンドウの矩形の境界
    int width;    // デバイスの幅
    int height;   // デバイスの高さ

    // グローバル変数構造体の全体をゼロに初期化する。
    memset(&myglobs, 0, sizeof(myglobs));

    // DirectDrawClipperオブジェクトを作成し、ウィンドウを関連付ける。
    rval = DirectDrawCreateClipper(0, &lpDDClipper, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create DirectDrawClipper object.");
        return FALSE;
    }
    rval = lpDDClipper->SetHWnd(0, win);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to set the window handle for the DirectDrawClipper.");
        return FALSE;
    }

    // Direct3D保持モードオブジェクトを作成する。
    rval = Direct3DRMCreate(&lpD3DRM);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create Direct3DRM.");
        return FALSE;
    }

    // Direct3D保持モードのデフォルトデバイスを作成する。
    GetClientRect(win, &rc);

    rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper,
                           NULL, // Default device
                           rc.right, rc.bottom, &myglobs.dev);

    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the D3DRM device.");
        return FALSE;
    }


    // WM_ACTIVATEとWM_PAINTメッセージを処理するため、
    // Direct3D保持モードのウィンドウデバイスを作成する。
    rval = myglobs.dev->QueryInterface(IID_IDirect3DRMWinDevice,
                                       (void **) &myglobs.windevice);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the window device.");
        return FALSE;
    }


    // マスターシーンフレームとカメラフレームを作成する。
    rval = lpD3DRM->CreateFrame(NULL, &myglobs.scene);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the master scene frame.");
        return FALSE;
    }

    rval = lpD3DRM->CreateFrame(myglobs.scene, &myglobs.camera);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the camera's frame.");
        return FALSE;
    }

    // デバイス、カメラフレーム、デバイスの幅と高さを使用して、
    // Direct3D保持モードのビューポートを作成する。
    width = myglobs.dev->GetWidth();
    height = myglobs.dev->GetHeight();

    rval = lpD3DRM->CreateViewport(myglobs.dev, myglobs.camera, 0, 0,
                                   width, height, &myglobs.view);
    if (FAILED(rval)) {
        myglobs.bInitialized = FALSE;
        RELEASE(myglobs.dev);
        return FALSE;
    }

    // レンダリングするシーンを作成する。
    if (!BuildScene(lpD3DRM, myglobs.dev, myglobs.scene,
                    myglobs.camera))
        return FALSE;

    // グローバル変数は初期化された。
    myglobs.bInitialized = TRUE;

    return TRUE;
}

InitApp

InitApp関数は、メインウィンドウクラスとメインウィンドウを作成する。これは、Windows アプリケーションでは一般的な処理である。InitApp から呼び出されるDirect3D保持モードは、CreateObjectsの呼び出しだけである。

InitApp関数ソースコード

// メインウィンドウを作成し、オブジェクトを初期化する。

static HWND InitApp(HINSTANCE this_inst, int cmdshow)
{
    HWND win;     // メインウィンドウハンドル
    WNDCLASS wc;

    // ウィンドウクラスを設定して登録する。
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = sizeof(DWORD);
    wc.hInstance = this_inst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "D3DRM Example";
    if (!RegisterClass(&wc))
        return FALSE;

    // ウィンドウの作成。
    win = CreateWindow(
            "D3DRM Example",                   // クラス
            "RMBegin2: Direct3DRM Sample Two", // キャプション
            WS_OVERLAPPEDWINDOW,               // スタイル
            CW_USEDEFAULT,                     // init. x pos
            CW_USEDEFAULT,                     // init. y pos
            350,                               // init. x size
            300,                               // init. y size
            NULL,                              // 親ウィンドウ
            NULL,                              // メニューハンドル
            this_inst,                         // プログラムハンドル
            NULL                               // 作成パラメータ
        );
    if (!win)
        return FALSE;

    // グローバル変数を初期化し、デバイスを列挙し、
    // Direct3D保持モードオブジェクトを作成する。
    if (!CreateObjects(win))
        return FALSE;

    // ウィンドウの表示。
    ShowWindow(win, cmdshow);
    UpdateWindow(win);

    return win;
}

WinMain

WinMain関数は、アプリケーションにメッセージループを提供する。グローバル変数が初期化されていて、アプリケーションが最小化されていなければ、WinMainはRenderSceneを使用してシーンをスクリーンに描画する。

WinMain関数ソースコード

// アプリケーションを初期化し、メッセージを処理して、シーンをレンダリングする。
int APIENTRY WinMain (HINSTANCE this_inst,
                      HINSTANCE prev_inst,
                      LPSTR cmdline,
                      int cmdshow)
{
    HWND    hwnd;
    MSG     msg;
    prev_inst;
    cmdline;

    // ウィンドウを作成し、オブジェクトを初期化する。
    if (!(hwnd = InitApp(this_inst, cmdshow)))
        return 1;

    while (TRUE) {
        // メッセージキューを監視して、メッセージを処理する。PeekMessageは
        // 制御を即座にアプリケーションに返すので、アプリケーションは
        // メッセージの処理とレンダリングを継続できる。
        // メッセージがWM_QUITであれば、アプリケーションが終了しているため、
        // この関数を抜ける。
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

            if (msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        // アプリケーションが最小化されておらず、Direct3D保持モード
        // オブジェクトが初期化されていれば、レンダリングを行う。
        if (!myglobs.bMinimized && myglobs.bInitialized) {

            // シーンを1フレーム、レンダリングする。レンダリングが失敗したら、
            // WM_DESTROYメッセージを発行する。これにより、アプリケーションが
            // 終了する前に、リソースを解放できる。
            if (!RenderScene()) {
                DISPLAYMSG("Rendering failed. Aborting execution.");
                PostMessage(NULL, WM_DESTROY, 0, 0);
                break;
            }

        // このアプリケーションが現在レンダリングをしていなければ、
        // 他のアプリケーションに制御を渡す。
        } else
              WaitMessage();

    }
    return msg.wParam;
}

WindowProc

WindowProcは、アプリケーションのメインウィンドウのメッセージを処理する。フラグをセットして、アプリケーションが最小化されているときはレンダリングを停止させる。さらに、この関数は、アプリケーションウィンドウのサイズ変更、ウィンドウのアクティブ化、描画、アプリケーションがシャットダウンしたときのオブジェクト解放を行う。

ユーザがアプリケーションウィンドウのサイズを変更すると、RMBegin2サンプルはシーンのサイズを増減することで、ウィンドウの画像を継続して表示させる。この関数は、以下の手順に従って、WM_SIZEメッセージ(ウィンドウのサイズ変更)を処理する。

WindowProcは、IDirect3DRMWinDevice::HandleActivateメソッドを使用して WM_ACTIVATE メッセージを、IDirect3DRMWinDevice::HandlePaintメソッドを使用して WM_PAINT メッセージを処理する。

WindowProc関数ソースコード

// メインウィンドウのメッセージを処理する。

LRESULT CALLBACK WindowProc(HWND win,
                            UINT msg,
                            WPARAM wparam,
                            LPARAM lparam)
{
HRESULT rval;

switch (msg) {

    case WM_SIZE:
        // ウィンドウのサイズ変更を処理する。アプリケーションが最小化されて
        // いれば、フラグをセットし、最小化時のレンダリングを停止し、
        // 関数から抜ける。それ以外の場合は、最小化フラグが
        // クリアされていることを確認して、最小化以外の
        // ウィンドウサイズの変更のための検査を行う。
        {
        if (SIZE_MINIMIZED == wparam)
        {
            myglobs.bMinimized = TRUE;
            break;
        }
        else
            myglobs.bMinimized = FALSE;

        // ウィンドウサイズの変更を処理する。ウィンドウの現在の幅と高さを
        // 取得する。
        int width = LOWORD(lparam);
        int height = HIWORD(lparam);

        // ビューポートやデバイスなどのグローバル変数が初期化されていれば、
        // ビューポートとデバイスの幅と高さを取得する。
        // 必要に応じて、新しいデバイスとウィンドウデバイス、またはビューポート
        // を作成する。新しいデバイスとウィンドウデバイスは、
        // ウィンドウが前のデバイスよりも大きな場合にだけ必要になる。
        if (myglobs.bInitialized)
        {
            int view_width = myglobs.view->GetWidth();
            int view_height = myglobs.view->GetHeight();
            int dev_width = myglobs.dev->GetWidth();
            int dev_height = myglobs.dev->GetHeight();

            // ウィンドウサイズの幅または高さが増えているか調べる。
            if (width > dev_width || height > dev_height)
            {
                // アプリケーションウィンドウは現在のデバイスよりも大きくなっている。
                // 前のビューポートとデバイスを破棄して、新しく作成する。
                // デバイスを解放する前に、ビューポートを解放すること。
                // この順序を間違えると、一般保護違反が発生する。
                myglobs.bInitialized = FALSE;
                RELEASE(myglobs.view);
                RELEASE(myglobs.dev);

                // デフォルトのDirect3D保持モードデバイスを新規作成する。
                rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper,
                                        NULL, // Default device
                                        width, height,
                                        &myglobs.dev);

                if (FAILED(rval))
                {
                    // 新しいデバイスが作成できない場合、ユーザに通知して、
                    // アプリケーションをシャットダウンする。
                    DISPLAYMSG("Failed to create the D3DRM device.");
                    PostMessage(NULL, WM_DESTROY, 0, 0);
                    return FALSE;
                }

                // 前のDirect3D保持モードウィンドウデバイスを破棄し、
                // 新規作成したデバイスと一致する新しいウィンドウデバイ
                // スを作成する。ウィンドウデバイスは、WM_ACTIVATEと
                // WM_PAINTメッセージを処理する。
                RELEASE(myglobs.windevice);
                rval = myglobs.dev->QueryInterface(
                                       IID_IDirect3DRMWinDevice,
                                       (void **) &myglobs.windevice);
                if (FAILED(rval)) {
                    DISPLAYMSG("Failed to create the window device.");
                    return FALSE;
                }

            }

            // ウィンドウのサイズが変更されていれば、前のビューポートを
            // 解放して、新しいビューポートを適切なサイズで作成する。
            myglobs.bInitialized = FALSE;
            RELEASE(myglobs.view);
            rval = lpD3DRM->CreateViewport(myglobs.dev,
                                           myglobs.camera,
                                           0, 0, width, height,
                                           &myglobs.view);
            if (FAILED(rval))
            {
                // 新しいビューポートが作成できない場合、ユーザに通知して、
                // アプリケーションをシャットダウンする。
                DISPLAYMSG("Failed to create a new viewport.");
                PostMessage(NULL, WM_DESTROY, 0, 0);
                break;
            }

            myglobs.bInitialized = TRUE;
        }
        }
        break;

    case WM_ACTIVATE:
        // ウィンドウデバイスのHandleActivateメソッドを使用して、
        // ウィンドウのアクティブ化を処理する。
        if (myglobs.bInitialized)
            if (FAILED(myglobs.windevice->HandleActivate(wparam)))
                DISPLAYMSG("Failed to handle WM_ACTIVATE.");
        break;

    case WM_PAINT:
        // グローバル変数が初期化されていれば、
        // ウィンドウデバイスのHandlePaintメソッドを使用して、
        // 再描画を処理する。
        if (!myglobs.bInitialized)
            return DefWindowProc(win, msg, wparam, lparam);

        // ウィンドウデバイスを使用して、再描画を処理する。
        RECT rc;
        PAINTSTRUCT ps;

        if (GetUpdateRect(win, &rc, FALSE)) {
            BeginPaint(win, &ps);
            if (FAILED(myglobs.windevice->HandlePaint(ps.hdc)))
                DISPLAYMSG("Failed to handle WM_PAINT.");
        }
        EndPaint(win, &ps);
        break;

    case WM_DESTROY:
        // bInitializedフラグをクリアし、オブジェクトを解放して、
        // WM_QUITメッセージを発行して、アプリケーションを終了する。
        // デバイスを解放する前に、ビューポートを解放すること。
        // この順序を間違えると、一般保護違反が発生する。
        myglobs.bInitialized = FALSE;
        RELEASE(myglobs.view);
        RELEASE(myglobs.windevice);
        RELEASE(myglobs.dev);
        RELEASE(lpD3DRM);
        RELEASE(lpDDClipper);
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(win, msg, wparam, lparam);
    }
    return 0L;
}

関連トピック

ここではDirect3D保持モードを使用してオブジェクトを作成し、スクリーンに配置する基本を学習した。Direct3D保持モードのサンプルコードが、前よりも簡単に理解できるようになったであろう。ほとんどのサンプルは、RMMainが提供するヘルパコードを使用してデバイスを作成し、シーンをレンダリングしているが、独自のBuildScene関数により、それぞれのシーンを作成している。

次のステップとしては、EggGlobeなどのDirect3D保持モードサンプルを見るとよい。このサンプルは、RMBegin1RMBegin2と似た機能を持つ。

もう1つのDirect3D保持モードの一般的なタスクについては、「デバイスの列挙:RMEnumサンプル」を参照すること。この節では、RMEnumのサンプルコードのウォークスルーを行う。RMEnumは、デバイスを列挙する方法に焦点を当てている。

保持モードサンプルのすべての概要については、「サンプル」を参照すること。


トップに戻る トップに戻る
© 1999 Microsoft and/or its suppliers. All rights reserved. Terms of Use.