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

Direct3DRM Quick Start

これだけのシンプルなコードでこの 3D の描画を可能にしています。たかだかタマゴと侮ってはいけません、ちゃんとライティングされ、滑らかに陰影が付き(グーロー シェーディング)、回転しています。RMCanvas コントロールの関数やプロパティの仕様は Appendix を参照して下さい。RMCanvas は初期化だけではなく、マウスがポイントした 3D データの取得や、回転操作など他の処理も簡単にします。この RMCanvas の内容を説明するより、それを使ったサンプルコードを説明する方がよりわかりやすいでしょう、次の XYZGraph サンプルでその有効性を紹介しましょう。

Excel データを 3Dグラフ化するコントロール XYZGraph

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: 書き込み可能