Direct3D Retained Mode for Visual Basic
Hiroyuki Kawanishi (川西 裕幸)
マイクロソフト株式会社
テクニカル エバンジェリスト
April 27, 2000
DirectX 7 SDK には、3D グラフィックスを Visual Basic から扱う、しかも初期化を容易にするラッパーがサンプルコードとして用意されています。これを使えば十数行のコーディングで 3D オブジェクトの表示・回転ができます。ここでは Excel データ 3D 表示のカスタム コントロールのサンプルを紹介します。なお、この記事で解説するサンプルは、デフォルトでは、全て mssdk\samples\Multimedia\VBSamples\D3DRM 以下にインストールされます。
このイメージは SDK に入っているサンプルコード PartsDB を日本語化して実行した結果です。このような VB が得意とするユーザー インターフェイスの中に Direct3D 保持モード(RM) を使って 3D 描画をするのは DirectX for Visual Basic の非常に上手な使い方です。3D 描画されているパーツをクリックすればパーツナンバーやそのプロファイルが左下に表示されます。DirectX はゲームだけではなくこのような使い方も可能だという良い例です。
SDK のサンプルコードではこういったアプリケーションのサンプルだけではなく Direct3D RM の初期化を簡単にするラッパーや、Excel データを 3D 表示するカスタム コントロールなどのサンプルが含まれています。ここでは次の 2 つのコントロールについて説明します。
目次
- RM の初期化を容易にするコントロール RMControl.ocx
- Excel データを 3D グラフ化するコントロール XYZGraph
- Appendix
RM の初期化を容易にするコントロール RMControl.ocx
DirectX は初期化が難しいとよく言われます (事実そうです) が、このコントロールを使うことで非常に初期化が簡単になります、次 の QuickStart サンプル コードと描画結果のイメージをご覧下さい。
Option Explicit
Private Sub Form_Load()
Dim frame As Direct3DRMFrame3
Dim mesh As Direct3DRMMeshBuilder3
With RMCanvas1
.StartWindowed
Set frame = .D3DRM.CreateFrame(.SceneFrame)
Set mesh = .D3DRM.CreateMeshBuilder()
mesh.LoadFromFile App.Path + "\egg.x",
0, D3DRMLOAD_FROMFILE, Nothing, Nothing
frame.AddVisual mesh
frame.SetRotation Nothing, 1, 1, 1, 0.04
End With
Timer1.Interval = 1
End Sub
Private Sub Timer1_Timer()
RMCanvas1.Update
End Sub
これだけのシンプルなコードでこの 3D の描画を可能にしています。たかだかタマゴと侮ってはいけません、ちゃんとライティングされ、滑らかに陰影が付き(グーロー シェーディング)、回転しています。RMCanvas コントロールの関数やプロパティの仕様は Appendix を参照して下さい。RMCanvas は初期化だけではなく、マウスがポイントした 3D データの取得や、回転操作など他の処理も簡単にします。この RMCanvas の内容を説明するより、それを使ったサンプルコードを説明する方がよりわかりやすいでしょう、次の XYZGraph サンプルでその有効性を紹介しましょう。
Excel データを 3Dグラフ化するコントロール XYZGraph
これは Excel に貼り付けたカスタムコントロール XYZGraph の描画イメージです、Excel のデータを 3D グラフとして表示しています、ポイントしたデータを数値表示し、もちろんマウスによる回転も可能です。DirectX を使った Excel データの表示なんて難しそうに感じるかもしれませんが、上記の RMCanvas を使えばコーディングは簡単です。ではこのサンプル プログラムの中身を説明していきましょう。
- メイン
- Direct3D RM 初期化
- データ セットアップ
- マウス ポインタからデータを取得
- マウスの動きに合わせて回転
メイン
GraphCells がこのコンポーネントのメイン部分です、ここでRMの初期化とデータのセットアップを行い、最後に Render で 3D 描画を行っています。最初にチェックしているのはカラー デプスです、RMCanvas は 256 色では動かないので、それをチェックしています。
' セルデータから3Dグラフを描画
Public Sub GraphCells(r As Range)
Dim i As Integer
m_binit = False
Dim dx7 As New DirectX7
' カラー デプスのチェック
If dx7.SystemBpp <= 8 Then
MsgBox
"This control designed to run on high color displays"
Exit Sub
End If
' ポイントデータの初期化
ClearPoints
Set m_range = r
' データの総数
m_nRows = r.Rows.Count
' RM ルートフレーム以下のデータを削除
DestroyOld
' D3DRM の初期化
Start3D
' セルデータをラベルにセット
m_labelX = m_range.Cells(1, 1)
m_labelY = m_range.Cells(1, 2)
m_labelZ = m_range.Cells(1, 3)
' データ配列にセルデータをセット
For i = 2 To m_nRows
AddPoint CSng(m_range.Cells(i, 1)),
CSng(m_range.Cells(i, 2)),
CSng(m_range.Cells(i, 3))
Next
' フレームとメッシュの配列データをアロケート
AllocateMemory
' 全ポイントデータにボックスメッシュとフレームをセット
CreatePoints
' ボックスメッシュのフレームを3D座標に配置
UpdatePointDataFromPoints
' 背景の目盛をセット
CreateBackDrop
' フラグのセット
m_binit = True
m_bInitFromCells = True
' レンダリング
Render
End Sub
RM 初期化
前述したように RM の初期化は RMCanvas によって非常に簡単になっています、とはいえいくつかの初期化が必要です、Start3D サブルーチンがそれを行っています。まずウィンドウ モード、背景色、テクスチャ マッピング、環境光、回転すべきフレームのセット、ディレクショナル ライト等をここで初期化します。
' D3DRM の初期化
Private Sub Start3D()
' 表示可能にするフラグ
RMCanvas1.Visible = True
Dim m As Direct3DRMMeshBuilder3
' ウィンドウモードで表示
RMCanvas1.StartWindowed
Set d3drm = RMCanvas1.d3drm
Set scene = RMCanvas1.SceneFrame
' 背景色を白にセット
scene.SetSceneBackgroundRGB 1, 1, 1
' テクスチャの張り方をリニアに設定
RMCanvas1.Device.SetTextureQuality D3DRMTEXTURE_LINEAR
' 環境光をセット
RMCanvas1.AmbientLight.SetColorRGB 0.2, 0.2, 0.2
' 回転させるフレームを設定
Set m_pivot = d3drm.CreateFrame(scene)
Set m_root = d3drm.CreateFrame(m_pivot)
m_root.AddScale D3DRMCOMBINE_REPLACE, 5, 5, 5
' ディレクショナル ライトの位置をセット
RMCanvas1.DirLightFrame.SetPosition Nothing, 0, -1, -10
' ライトフレームの向きをセット
RMCanvas1.DirLightFrame.LookAt m_root, Nothing, 0
End Sub
データ セットアップ
Excel から取得したデータを 3D データにセットアップします。最初に CreatePoints サブルーチンでデータの数だけ 3Dデータ(ボックス メッシュ)を作成し、UpdatePointDataFromPoints サブルーチンで SetPosition メソッドを使い、そのプリミティブを3D座標に移動します。XYZ に -0.5 が含まれているのは表示している座標系が ( 座標 0.0 ではなく ) グラフの中心( 0.5, 0.5, 0.5 ) を原点にしているからです。
Direct3D RM では 3D オブジェクトをメッシュと表現し、AddVisual メソッドを使ってフレームと結合するとオブジェクトの描画や操作が可能になります。カメラやライトも同様にフレームと結合して使用可能になります。
UpdatePointDataFromPoints の後半で作成しているメッシュは Y 平面からデータのボックスに伸びている柱です。
' 全ポイントデータにボックスメッシュとフレームをセット
Private Sub CreatePoints()
Dim i As Integer
For i = 1 To m_nPoints
' フレームを作成
Set m_pointFrame(i) = d3drm.CreateFrame(m_root)
' メッシュを作成
Set m_pointMesh(i) =
RMCanvas1.CreateBoxMesh(1, 1, 1)
' スケーリング
m_pointMesh(i).ScaleMesh 0.05, 0.05, 0.05
' 色
m_pointMesh(i).SetColorRGB 0, 1, 0
' フレームとメッシュを結合
m_pointFrame(i).AddVisual m_pointMesh(i)
Next
End Sub
' セルデータから3Dグラフをセットアップ
Private Sub UpdatePointDataFromPoints()
…..
For i = 1 To m_nPoints
' 正規化した座標で XYZ をセット
X = (-m_minX + m_Points(i).X) / m_spreadX
Y = (-m_minY + m_Points(i).Y) / m_spreadY
z = (-m_minZ + m_Points(i).z) / m_spreadZ
' フレームの位置座標と名前をセット
m_pointFrame(i).SetPosition
m_root, X - 0.5, Y - 0.5, z - 0.5
m_pointMesh(i).SetName "point " + Str(i)
' 底面からポイントに伸びる柱をセット
Dim mb2 As Direct3DRMMeshBuilder3
' ボックスメッシュを作成
Set mb2 = RMCanvas1.CreateBoxMesh(0.01, Y, 0.01)
' 移動
mb2.Translate 0, -Y / 2, 0
' 色
mb2.SetColor &H20001616
' ポイントのフレームに結合
m_pointFrame(i).AddVisual mb2
Next
End Sub
マウスポインタからデータを取得
2D のスクリーンの座標から、描画されている 3D オブジェクトの情報を取得することは通常かなり面倒なことです。それはスクリーンの 2D ビュー座標から 3D のワールド座標あるいはローカル座標への逆変換が必要だからです。RMCanvas ではこれを PickTopMesh メソッドだけで可能にします。RMCanvas1_MouseMove では PickTopMesh を使ってポイントされたフレーム名を取得し、その名前に含まれているデータ ID を引き出し、配列に格納してあった元データをゲットしています。
' ポインタの指したボックスからXYZデータを表示
Private Sub RMCanvas1_MouseMove (Button As Integer,
Shift As Integer, X As Single, Y As Single)
Dim mb As Direct3DRMMeshBuilder3
Dim p As Long
Dim strName As String
' ポインタの XY からそのフレーム名を取得
Set mb = RMCanvas1.PickTopMesh(CLng(X), CLng(Y))
If mb Is Nothing Then Exit Sub
strName = mb.GetName()
' 何もなければexit
If strName = "" Then
Text1.Visible = False
Exit Sub
End If
' points 名かどうかを確認
If InStr(strName, "points") <> 0 Then Exit Sub
' 7文字目の数字を取得
p = Val(Mid$(strName, 7))
Text1.Visible = True
' 配列からデータを取得し、text1に
With m_Points(p)
Text1.Text = m_labelX + "=" + Str(.X) + ":
" + m_labelY + "=" + Str(.Y) + ":
" + m_labelZ + "=" + Str(.z)
End With
End Sub
マウスの動きに合わせて回転
マウスの動きに合わせて回転操作をすることも通常面倒な作業です、マウスのイベントを取得し、XY の移動量から回転行列を計算し、フレームにその回転の座標変換を施さなければなりません。RMCanvas を使うと RotateFrame メソッドだけでマウスに合わせた回転が可能です。
回転方法1 マウスダウン後の操作
Private Sub RMCanvas1_MouseDown
(Button As Integer, Shift As Integer, X As Single, Y As Single)
' マウスによる回転をセット
Set RMCanvas1.RotateFrame = m_root
m_bMouseDown = True
' 右ボタンならポップアップメニューを表示
If Button = 2 Then
PopupMenu MENU_POP
End If
End Sub
まとめ
いかがでしょうか、DirectX を使って Excel 用の 3D 描画カスタム コントロールを作成することは決して難しいことではないことがおわかりいただけたでしょうか? RMCanvas という力強いラッパーを使えば、初期化、データ取得、回転などの面倒な操作が非常に簡略化されます。RMCanvas も XYZGraph もDirectX 7 SDK に含まれているサンプルコードですから、コピーして使っていただくことも、これを参考にして自身のプログラム開発に役立てることもできます。
Appendix
RMCanvas の関数
関数名 | 機能 |
---|---|
StartWindowed | ウィンドウ モードで開始 |
InitFullScreen | 幅、高さ、bpp を与えられ、フルスクリーンモードで開始 |
InitWindowed | シミュレーション・アニメーションをインクリメント |
Tick | ウィンドウ モードで開始 |
Render | シーンをレンダリング |
Update | Tick と Render を結合する |
GetBltRect | 更新された矩形を取得 |
CreateBoxMesh | 3 次元のメッシュを作成 |
CreateSheetMesh | 片面または両面の矩形ポリゴンを作成 |
GetBoundingBox | フレームのバウンディングボックスを取得 |
PickTopMesh | XY スクリーン座標からメッシュを取得 |
PickTopFrame | XY スクリーン座標からフレームを取得 |
RotateFromXY | XY スクリーン座標の回転 ( UI 用) |
CreateUpdateableTexture | 変更が可能なテクスチャを作成 |
RMCanvas のプロパティ
プロパティ名 | 属性 | 機能 |
---|---|---|
hWnd | r | 制御 hwnd を返す |
FPS | r | フレーム/秒を返す (更新の呼び出しから) |
SceneSpeed | rw | 描画速度を返す |
IsFullScreen | r | フルスクリーンのとき真 |
DisplayModes | r | 使用可能なディスプレイモードのリストを返す |
VideoCards | r | 使用可能なカードのリストを返す |
Deveices | r | 使用可能なレンダラのリストを返す |
Device | r | 現在の RMdevice を返す |
Viewport | r | 現在の( CameraFrame にフックされた) RMviewport を返す |
SceneFrame | r | シーングラフのルート フレーム |
DirLightFrame | r | DirLight を格納するフレーム(ノード)を返す |
DirLight | r | デフォルトのディレクショナル ライトを返す |
CameraFrame | r | カメラを表すフレームを返す |
AmbientLight | r | AmbientLight オブジェクトを返す |
DX | r | DirectX オブジェクトを返す |
DDRaw | r | DDraw オブジェクトを返す |
BackBuffer | r | 書き込みに使われる DDrawSurface を返す |
D3DRM | r | D3DRM オブジェクトを返す |
DirectDrawGuid | r | 現在使われている DirectDraw オブジェクトを返す |
Direct3DGuid | r | 現在使われている D3D ラスタライザを返す |
RotateFrame | rw | UI 回転の可否をセット |
RotateMode | rw | UI での回転のしかたを設定 |
RotateRadius | rw | UI での回転の速度を設定 |
Use3DHardware | rw | False の場合は強制的にソフトウェアでラスタライズ |
UseBackbuffer | rw | False の場合はバックバッファを使用しない |
※ r: 読み込み可能, w: 書き込み可能