デバイスの作成、シーンの構築と移動は、Microsoft® Direct3D®保持モードの基本的な概念である。RMBegin1サンプルでは、デフォルトのデバイスを作成し、Microsoft DirectX®ファイルからメッシュをロードし、オブジェクトに色を追加し、シーンに照明を追加し、オブジェクトを回転することで、この基本的な概念を具体的に示す。サンプルでは、シーンの構築時にメッシュビルダ、フレーム、照明、マテリアルを使用する。さらに、DirectDrawClipperオブジェクト、ビューポート、カメラも作成する。
この項では、RMBegin1サンプルのコードのウォークスルーを行う。サンプルソースコードは、必要な要素をすべて備えた1つのファイル (RMBegin1.cpp) で構成されており、Microsoft Windows® DirectDraw®およびDirect3D保持モードの標準ヘッダファイル以外の個別のヘッダファイルは使用していない。これは、わかりやすくするためである。メニューやアイコンなどのリソースも使用していない。
このサンプルは、DirectX Foundation SDK (Software Development Kit) のTeapot.xファイルを使用して、シーンにティーポットの画像を与える。デフォルトでは、SDKをインストールすると、SDKのセットアッププログラムは、環境変数をSDKのメディアディレクトリに設定する。Teapot.xファイルはメディアディレクトリにあるので、アプリケーションはファイルを自動的に検出する。SDKをインストールしないでRMBegin1.exeを実行する場合、Teapot.xファイルはRMBegin1.exeと同じディレクトリに置く必要がある。
この項は、ユーザがCまたはC++のプログラミング経験があること、また、メッセージループ、ウィンドウの作成、メニューの使用など、Windowsプログラミングの基本概念に精通していることを前提にしている。
この項は、次の項目に分かれている。
#define INITGUID
#include <windows.h> // 標準 Windows ヘッダファイル #include <direct.h> // DirectDraw の定義 #include <d3drmwin.h> // Direct3D 保持モードの定義
このサンプルでは、いくつかの定数とグローバル変数を定義して、情報を整理する。
アプリケーションで定義されている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構造体は、デバイス、ビューポート、シーン、カメラに関する情報に加えて、アプリケーションの最小化やオブジェクトの初期化に関する情報も収集する。
// グローバル変数 LPDIRECT3DRM lpD3DRM = NULL; // Direct3DRMオブジェクト LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // DirectDrawClipperオブジェクト // グローバル構造体 struct _myglobs { // Direct3D保持モードデバイス LPDIRECT3DRMDEVICE dev; // シーンを表示するDirect3D保持モードビューポート LPDIRECT3DRMVIEWPORT view; // 他のフレームを配置するマスターフレーム LPDIRECT3DRMFRAME scene; // ユーザの視点を記述するフレーム LPDIRECT3DRMFRAME camera; // アプリケーションは最小化されている。 BOOL bMinimized; // Direct3D保持モードの全オブジェクトが初期化されている。 BOOL bInitialized; } myglobs;
RMBegin1.cppには、以下の関数が含まれている。
BuildScene関数は、レンダリングするシーンを作成する。DirectXファイルからメッシュをロードし、色をメッシュに追加し、シーンに照明を追加する。さらに、BuildSceneは、オブジェクトの位置と回転の方法を設定する。グローバル変数を初期化し、デフォルトのデバイス、フレーム、ビューポートを作成するCreateObjectsが、この関数を使用する。
BOOL BuildScene(LPDIRECT3DRM, LPDIRECT3DRMDEVICE, LPDIRECT3DRMFRAME, LPDIRECT3DRMFRAME);
RenderScene関数は、BuildSceneが作成したシーンを描画する。WinMainがこの関数を使用するたびに、RenderSceneはシーンを移動して、オブジェクトに回転効果を与える。
static BOOL RenderScene(void);
CreateObjects関数は、グローバル変数を初期化して、デフォルトのデバイス、カメラフレーム、シーンフレーム、ビューポートを作成する。CreateObjectsはBuildSceneを使用して、レンダリングするシーンを作成する。InitAppがCreateObjectsを使用する。
BOOL CreateObjects(HWND win);
InitApp関数は、Windowsアプリケーションの標準的なコードにより、ウィンドウクラスとアプリケーションのウィンドウを作成する。InitAppは、CreateObjectsを使用して、グローバルオブジェクトを初期化する。
static HWND InitApp(HINSTANCE, int);
WinMain関数は、アプリケーションのメッセージループを提供する。InitAppを使用してアプリケーションを初期化し、RenderSceneを使用してシーンを描画する。
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
WindowProc関数は、アプリケーションのメッセージを処理する。アプリケーションの最小化や破棄を処理する。
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
以上の関数の詳細については、この項の次の項目を参照すること。
BuildScene関数は、レンダリングするシーンを作成する。Direct3D保持モードのすべてのアプリケーションはシーンを作成する必要があるが、必要なコードはシーンに依存する。アプリケーションは通常、フレームと照明を作成し、視覚的な要素をシーンに追加し、オブジェクトの位置を設定する。
この節では、BuildScene関数のコードを部分的に示して、RMBegin1サンプルがシーンを構築する手順について説明する。さらに、RMBegin1サンプルのBuildScene関数に関するすべてのソースコードも示す。エラーが発生した場合は、goto文で関数の最後にジャンプして、関数から返る前に、すべてのオブジェクトを適切に解放する。
if (FAILED(lpD3DRM->CreateMeshBuilder(&meshbuilder))) goto generic_error; rval = meshbuilder->Load("teapot.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL);
if (FAILED(lpD3DRM->CreateFrame(scene, &childframe))) goto generic_error;
if (FAILED(childframe->AddVisual((LPDIRECT3DRMVISUAL)meshbuilder))) goto generic_error;
rval = camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), -D3DVAL(7));
if (FAILED(childframe->SetRotation(scene, D3DVAL(0), D3DVAL(1), D3DVAL(0), D3DVAL(0.03)))) // 角度 goto generic_error;
if (FAILED(lpD3DRM->CreateFrame(scene, &lights))) goto generic_error;
if (FAILED(lights->SetPosition(scene, D3DVAL(5), D3DVAL(0), -D3DVAL(7)))) goto generic_error;
if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(0.8), D3DVAL(0.9), &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->CreateMaterial(D3DVAL(10.0), &mat))) goto generic_error; if (FAILED(meshbuilder->SetMaterial(mat))) goto generic_error;
if (FAILED(meshbuilder->SetColorRGB(D3DVAL(0.0), // 赤 D3DVAL(0.7), // 緑 D3DVAL(0.0)))) // 青 goto generic_error;
RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat);
// レンダリングするシーンを作成する。 BOOL BuildScene(LPDIRECT3DRM lpD3DRM, LPDIRECT3DRMDEVICE dev, LPDIRECT3DRMFRAME scene, LPDIRECT3DRMFRAME camera) { LPDIRECT3DRMFRAME lights = NULL; LPDIRECT3DRMMESHBUILDER meshbuilder = NULL; LPDIRECT3DRMFRAME childframe = NULL; LPDIRECT3DRMLIGHT light1 = NULL; LPDIRECT3DRMLIGHT light2 = NULL; LPDIRECT3DRMMATERIAL mat = NULL; HRESULT rval; // irectX ファイルからメッシュをロードする。 if (FAILED(lpD3DRM->CreateMeshBuilder(&meshbuilder))) goto generic_error; rval = meshbuilder->Load("teapot.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL); if (FAILED(rval)) { DISPLAYMSG("Failed to load .x file."); goto ret_with_error; } // シーンの子フレームを作成する。 if (FAILED(lpD3DRM->CreateFrame(scene, &childframe))) goto generic_error; // ロードしたメッシュを子フレームに追加する。 if (FAILED(childframe->AddVisual((LPDIRECT3DRMVISUAL)meshbuilder))) goto generic_error; // カメラフレーム位置を設定する。カメラと同じ x 値と // y 値を持つオブジェクトは、まっすぐに表示される。 // z 値がマイナスであると、オブジェクトは遠ざかり、 // 負数の絶対値が大きくなるほど、オブジェクトは小さくなる。 rval = camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), -D3DVAL(7)); if (FAILED(rval)) { DISPLAYMSG("Failed to position the camera in the frame."); goto ret_with_error; } // 子メッシュを y 軸に沿って回転させる (x 値と z 値はゼロ)。 // 小さな回転角を使用して、ゆっくり回転させる。 if (FAILED(childframe->SetRotation(scene, D3DVAL(0), D3DVAL(1), D3DVAL(0), D3DVAL(0.03)))) // angle goto generic_error; // シーンの照明を初期化する。シーンの子フレームになる // 照明フレームを作成する。 if (FAILED(lpD3DRM->CreateFrame(scene, &lights))) goto generic_error; // 照明フレームをシーンに配置する。光はカメラの右側から照らす。 // x 値は異なるが、y 値と z 値はカメラ位置と同じである。 if (FAILED(lights->SetPosition(scene, D3DVAL(5), D3DVAL(0), -D3DVAL(7)))) goto generic_error; // 明るいパラレルポイント照明を作成して、照明フレームに追加する。 // カラー値は、0.0 (暗い) から1.0 (明るい) までの範囲とする。 if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(0.8), D3DVAL(0.9), &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; // マテリアルを作成し、前にロードしたメッシュの反射特性を設定する // (5.0 は金属的な外観になり、それ以上の値では、より柔らかい外観となる)。 if (FAILED(lpD3DRM->CreateMaterial(D3DVAL(10.0), &mat))) goto generic_error; if (FAILED(meshbuilder->SetMaterial(mat))) goto generic_error; // メッシュの色を設定する。ここでは明るい緑にする。 if (FAILED(meshbuilder->SetColorRGB(D3DVAL(0.0), // 赤 D3DVAL(0.7), // 緑 D3DVAL(0.0)))) // 青 goto generic_error; // 終了処理。 RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat); return TRUE; generic_error: DISPLAYMSG("A failure occured while building the scene."); ret_with_error: RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat); return FALSE; }
RenderScene関数は、実際にシーンをスクリーンに描画する。BuildSceneが作成したシーンを次の手順に従って描画する。
// ビューポートをクリアし、次のフレームをレンダリングして、ウィンドウを更新する。 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は、サンプルのグローバル変数を初期化し、オブジェクトを作成する。グローバル変数を初期化した後、デバイスの作成に必要な手順を実行し、デバイスをアプリケーションのメインウィンドウに関連付け、マスターシーンフレーム、カメラフレーム、ビューポートを作成する。以下の手順に従う。
// グローバル変数を初期化して、デバイスとオブジェクトを作成する。 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, // デフォルトデバイス rc.right, rc.bottom, &myglobs.dev); if (FAILED(rval)) { DISPLAYMSG("Failed to create the D3DRM 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保持モードのビューポートを作成する。 wid 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関数は、メインウィンドウクラスとメインウィンドウを作成する。これは、Windowsアプリケーションでは一般的な処理である。InitAppから呼び出されるDirect3D保持モードは、CreateObjectsの呼び出しだけである。
// メインウィンドウを作成し、オブジェクトを初期化する。 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", // クラス "RMBegin1: Direct3DRM Sample One", // キャプション 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はRenderSceneを使用してシーンをスクリーンに描画する。
// アプリケーションを初期化し、メッセージを処理して、シーンをレンダリングする。 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は、アプリケーションのメインウィンドウのメッセージを処理する。フラグをセットして、アプリケーションが最小化されているときはレンダリングを停止させる。さらに、この関数は、アプリケーションがシャットダウンしたときにオブジェクトを解放する。
// メインウィンドウのメッセージを処理する。 LRESULT CALLBACK WindowProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_SIZE: // ウィンドウの最小化を処理する。アプリケーションが最小化されて // いれば、フラグをセットして、最小化時にはレンダリングを停止する。 // それ以外の場合は、レンダリングを継続する。 if (SIZE_MINIMIZED == wparam) myglobs.bMinimized = TRUE; else myglobs.bMinimized = FALSE; break; case WM_DESTROY: // bInitializedフラグをクリアし、オブジェクトを解放して、 // WM_QUITメッセージを発行して、アプリケーションを終了する。 // デバイスを解放する前に、ビューポートを解放すること。 // この順序を間違えると、一般保護違反が発生する。 myglobs.bInitialized = FALSE; RELEASE(myglobs.view); RELEASE(myglobs.dev); RELEASE(lpD3DRM); RELEASE(lpDDClipper); PostQuitMessage(0); break; default: return DefWindowProc(win, msg, wparam, lparam); } return 0L; }
ここではDirect3D保持モードを使用してオブジェクトを作成し、スクリーンに配置する基本を学習した。Direct3D保持モードのサンプルコードが、前よりも簡単に理解できるようになったであろう。ほとんどのサンプルは、RMMainが提供するヘルパコードを使用してデバイスを作成し、シーンをレンダリングしているが、独自のBuildScene関数により、それぞれのシーンを作成している。
次のステップとしては、RMBegin1の機能を拡張した、RMBegin2サンプルを見るとよい。また、RMBegin1と似た機能を持つEggやGlobeなどのDirect3D保持モードサンプルを調べてもよい。
もう一つのDirect3D保持モードの一般的なタスクについては、「デバイスの列挙:サンプルRMEnum」を参照すること。この節では、RMEnumのサンプルコードのウォークスルーを行う。RMEnumは、デバイスを列挙する方法に焦点を当てている。
保持モードサンプルのすべての概要については、「サンプル」を参照すること。
トップに戻る
© 1999 Microsoft and/or its suppliers. All rights reserved. Terms of Use.