.. :tocdepth:2
====================================
マルチビュー
====================================
ここではサンプルプロジェクト MultiViewSample を通して、1つのシーンを複数のビューで表示する方法について解説します。
つまり同じ3Dモデルを複数の視点から見ることが可能で、3面図などへの応用も可能です。
リソース
=============================
WPFにおいて、複数のUI要素から利用する共通のオブジェクトを使用する仕組みとしてリソースという仕組みがあります。
本サンプルではリソースとしてMoNo.RAILのシーングラフを設定することで、複数の3Dビューから同一のシーングラフを参照しています。
サンプルプログラムのMainWindow.xamlを見ると、Windowのリソースとしてシーングラフを登録し、2つの ``GLViewport`` から参照している構造が見て取れます。
.. code-block:: xml
...
...
...
ではこれを踏まえてサンプルプロジェクトを見ていきましょう。
MainWindow.xaml
=============================
MainWindow.xamlがサンプルプロジェクトのビューになります。
XAML全体は他のサンプルに比べ少々大きめなので要点毎にXAMLの一部を切り出して解説します。
XML名前空間
-----------------------------
.. code-block:: xml
...
xmlns:m="http://rail.monocommunity.com"
...
``m`` を接頭辞とすることでMoNo.RAILに定義されたクラスにアクセスできるようにします。
ビューモデルの設定
-----------------------------
.. code-block:: xml
...
...
WindowのDataContextにビューモデル ``MainViewModel`` を設定しています。
``MainViewModel`` はMainViewModel.csで定義されており、これは1つの三角形を表示するシーン ``Triangle`` だけをプロパティとして持ちます。
.. code-block:: csharp
class MainViewModel
{
public MoNo.Graphics.IScene Triangle { get; } =
GraphicsUT.CreateScene(GLPrimType.Triangles, new[]
{
new Point3f(1, 0, 0),
new Point3f(0, 1, 0),
new Point3f(0, 0, 1),
});
}
リソースの設定
-----------------------------
.. code-block:: xml
...
...
複数(このサンプルでは2つ)のUI要素共通のデータとしてMoNo.RAILの ``SceneGraph`` クラスを *sceneGraph* をキーにしてリソースに登録します。
シーングラフには1つの ``Entry`` を設定し、そのオブジェクトにはビューモデルの ``Triangle`` をBindします。
また色をピンクに設定しています。
左のビュー
----------------------------
.. code-block:: xml
...
...
まず1つ目のビューです。
暗い青を背景色として ``GLViewport`` を設置しています。
そしてシーングラフにはリソースに登録したシーングラフをStaticResourceマークアップ拡張で設定しています。
さらに左のビューにはMoNo.RAILで用意されている ``AxisScene`` という座標系を描画するシーンを ``WorldScenes`` に埋め込んでいます。
``WorldScenes`` はワールド座標系のシーンのコレクションで、シーンを複数配置することができます。
.. image:: multiview_sample_left.png
この ``WorldScenes`` は ``SceneGraph`` とは別に管理されるため、この座標系のように3Dビュー内の補助的な情報を描画するのに向いています。
また、ワールド座標系に配置されるので画面の回転やズームなどのカメラ操作の影響を受けます。
右のビュー
-----------------------------
.. code-block:: xml
...
...
次に2つ目のビューです。
茶色を背景色として ``GLViewport`` を配置しています。
左のビューと同様にリソースに登録してあるシーングラフを設定していますので、左のビューに表示されている三角形と同じものが表示されます。
そして右のビューにはサンプルプロジェクト内で定義しているシーン ``ScreenRectScene`` を ``BackgroundScenes`` に埋め込んでいます。
(後で見ますが、 ``ScreenRectScene`` はカーキの四角形を描画するシーンです。)
``BackgroundScenes`` は、背景として描画されるスクリーン座標系のシーンのコレクションです。
また背景として描画されるため、常にシーングラフの背後に描画されます。
.. image:: multiview_sample_right.png
同様に ``GLViewport`` には ``ForegroundScenes`` という、前景として描画されるスクリーン座標系のシーンコレクションも持っています。
試しに上記XAMLの ``BackgroundScenes`` を ``ForegroundScenes`` に変更して実行してみて下さい。
四角形が常に三角形シーンの前景として描画されるはずです。
.. image:: multiview_sample_right2.png
これらはスクリーン座標系に表示されるため、カメラ操作の影響を受けません。
常に表示しておきたい情報などを描画するのに向いているでしょう。
ScreenRectScene
=============================
最後にScreenRectSceneを見ておきましょう。
.. code-block:: csharp
...
class ScreenRectScene : System.Windows.DependencyObject, MoNo.Graphics.IScene
...
まず ``System.Windows.DependencyObject`` を継承してXAMLに埋め込めるようにしています。
また、``MoNo.Graphics.IScene`` を実装するMoNo.RAILのシーンでもあります。
.. code-block:: csharp
readonly MoNo.Graphics.IScene scene =
GraphicsUT.CreateScene(GLPrimType.Quads, new[]
{
new Point3f( 10, 10, 0 ),
new Point3f( 10, 90, 0 ),
new Point3f( 90, 90, 0 ),
new Point3f( 90, 10, 0 ),
});
単純な四角形のシーンを作成し保持しています。
なお、このシーンはXAMLでスクリーン座標系の背景として登録されますので、画面左上が原点となり、画面右向きがX軸の正の向き、画面下向きがY軸の正の向きとなることに留意しておきましょう。
.. code-block:: csharp
public void Draw( MoNo.Graphics.ISceneContext sc )
{
using (var scope = sc.Push())
{
scope.Color = System.Drawing.Color.Khaki;
scene.Draw(sc);
}
}
``IScene`` を実装するのに必要な ``Draw`` メソッドを実装しています。
描画前に、 ``ISceneContext`` の ``Push`` メソッドを呼び出すことで、``IScope`` の実装インスタンス ``scope`` を取得しています。
これは描画コンテキストの一部を局所的に変更するため、スタックにPushされる特殊なコンテキストだと考えて下さい。
ここでは色をカーキに変更し、保持していた ``scene`` を描画します。
usingステートメントを抜けると ``Dispose()`` が呼ばれることで、自動的にスタックからPopされ、以後この ``scope`` への変更が影響を与えることはなくなります。
このサンプルプログラムでは他に色を指定せず描画する要素がないため
.. code-block:: csharp
public void Draw( MoNo.Graphics.ISceneContext sc )
{
sc.Color = System.Drawing.Color.Khaki;
scene.Draw(sc);
}
と書いても動作は変わりません。
しかしこのようにしてしまうと描画コンテキストを使用する要素全てに影響が及んでしまうため、全く関係のないはずのシーンに影響を及ぼします。
これは意図せぬ副作用を生む厄介なバグの原因となりますので、個別の描画ではこのように直接コンテキストを変更することはせず、スコープを使用して局所的な変更に留めるようにして下さい。