HelixToolkit.Wpf.SharpDXでソリッド・ワイヤー・プロットの同時表示

HelixToolkit.Wpf.SharpDXを使用した3Dモデルをソリッド・ワイヤー・プロットを任意の組み合わせで重ねて表示する方法の共有です。
以前はHelixToolkit.Wpfで同じことを実現させようとしていたのですが、メッシュが多くなるとレンダリングが重くなり、満足いくパフォーマンスがでませんでした。そこで、HelixToolkit.Wpf.SharpDXへの移行を模索していたのですが、HelixToolkit.Wpfとではライブラリ構造が異なり、なおかつ英語も含め情報が少なく苦労したのでここに検証の成果を残しておこうと思います。

完成したトライアルプログラムのイメージ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/209026/ab5b7721-2aa0-e651-e128-659ea57b7622.png)
一つのオブジェクトの表示方法をソリッド・ワイヤー・プロットを切り替えられます。ソリッドとワイヤーを同時に表示するといったことも可能です。

# 検証環境
Windows 10 Pro 64bit
Visual Studio 2017
.Net Framework 4.5.2

# 事前準備
DirectX End-User Runtimesを以下のURLよりダウンロードし、インストールしておく。
https://www.microsoft.com/en-us/download/details.aspx?id=8109&e6b34bbe-475b-1abd-2c51-b5034bcdd6d2=True

# プロジェクトの作成
WPFアプリ(.NET Framework)の新規プロジェクトを作成します。C#, VB.netどちらでも構いませんが、サンプルコードはVB.netです。
C#を使用される方は、[Telerik Code Converter](http://converter.telerik.com/)でコンバージョンしてみてください。簡単なコードなので、精度よくコンバージョンできるはずです。
![2017-10-23 22_32_13-新しいプロジェクト.png](https://qiita-image-store.s3.amazonaws.com/0/209026/c519c98a-a70c-e8be-2e1e-ee4a6c97144c.png)
# nugetでHelixToolkit.Wpf.SharpDXをインストール
パッケージマネージャーコンソールを開き以下コマンドを実行します。
Install-Package HelixToolkit.Wpf.SharpDX

![image.png](https://qiita-image-store.s3.amazonaws.com/0/209026/7e480e9a-c90c-c8fa-ae74-0ae658ad8f07.png)

このときは以下の参照が追加されました。
Cyotek.Drawing.BitmapFont.1.3.2
HelixToolkit.1.0.0
HelixToolkit.Wpf.1.0.0
SharpDX.4.0.1
SharpDX.D3DCompiler.4.0.1
SharpDX.Direct3D9.4.0.1
SharpDX.DXGI.4.0.1
SharpDX.Direct2D1.4.0.1
SharpDX.Direct3D11.4.0.1
SharpDX.Direct3D11.Effects.4.0.1
SharpDX.Mathematics.4.0.1
HelixToolkit.Wpf.SharpDX.1.0.0

パッケージをインストールするとプロジェクト直下に以下3ファイルがコピーされます。
![2017-10-23 22_59_22-HelixTool-SharpDX.png](https://qiita-image-store.s3.amazonaws.com/0/209026/d71452c2-6d71-a4de-3e8f-b1a6e82f1e04.png)


このファイルをDebug, Releaseフォルダにコピーします。プロジェクトにファイルを含め、ビルド時にコピーするようにしてもかまいません。
sharpdx_direct3d11_1_effects_x64.dll
sharpdx_direct3d11_1_effects_x86.dll

# ViewModelの作成
MeshGeometry3D(ソリッド), LineGeometry3D(ワイヤー), PointGeometry3D(点)のプロパティを用意し、コンストラクタで3Dオブジェクトを作成します。また、座標面表示のために追加でLineGeometry3Dプロパティを使用します。

それぞれのオブジェクトの作成方法ですが、まずMeshGeometry3Dを作成し、LineGeometry3DとPointGeometry3Dは、MeshGeometry3DのPositions, Indicesプロパティを代入する方法で作成するのが楽です。サンプルで使用するボックス程度であれば、それぞれをゴリゴリとコーディングしても大したことはありませんが、複雑な3Dオブジェクトをそれぞれ作るのは手間です。

``` MainWindowViewModel.vb
'Copyright (c) 2017 Systeman
'This software is released under the MIT License, see https://opensource.org/licenses/mit-license.php

Imports System.Windows.Media
Imports HelixToolkit.Wpf.SharpDX
Imports HelixToolkit.Wpf.SharpDX.Core
Imports SharpDX
Imports System.ComponentModel

''' <summary>
''' MainWindowのViewModel
''' </summary>
Public Class MainWindowViewModel
    Implements ComponentModel.INotifyPropertyChanged
    Private _PropertyChangedHandler As PropertyChangedEventHandler
    Public Custom Event OnPropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
        AddHandler(ByVal value As PropertyChangedEventHandler)
            _PropertyChangedHandler = DirectCast([Delegate].Combine(_PropertyChangedHandler, value), PropertyChangedEventHandler)
        End AddHandler

        RemoveHandler(ByVal value As PropertyChangedEventHandler)
            _PropertyChangedHandler = DirectCast([Delegate].Remove(_PropertyChangedHandler, value), PropertyChangedEventHandler)
        End RemoveHandler

        RaiseEvent(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
            If _PropertyChangedHandler IsNot Nothing Then
                _PropertyChangedHandler(sender, e)
            End If
        End RaiseEvent
    End Event
    ''' <summary>
    ''' WPF用プロパティチェンジイベント
    ''' </summary>
    Friend Overridable Sub NotifyPropertyChanged(ByVal Information As String, Optional ByVal sender As Object = Nothing)
        If sender Is Nothing Then
            RaiseEvent OnPropertyChanged(Me, New PropertyChangedEventArgs(Information))
        Else
            RaiseEvent OnPropertyChanged(sender, New PropertyChangedEventArgs(Information))
        End If
    End Sub

    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    Sub New()
        '座標面の作成
        Dim gridLineBuilder As New LineBuilder
        GridGeometry3D = LineBuilder.GenerateGrid

        '座標面のトランスフォーム作成
        GridGeometry3DTransform = New Media3D.TranslateTransform3D(-5, -1, -5)

        'ソリッドオブジェクトを作成
        Dim meshBuilder As New MeshBuilder
        meshBuilder.AddBox(New Vector3(0, 0, 0), 2, 2, 2)
        MeshGeometry3D = meshBuilder.ToMeshGeometry3D
        MeshGeometry3D.Colors = New Color4Collection(MeshGeometry3D.TextureCoordinates.Select(Function(x)
                                                                                                  Return x.ToColor4()
                                                                                              End Function))

        'ワイヤーオブジェクト作成
        LineGeometry3D = New LineGeometry3D
        LineGeometry3D.Positions = MeshGeometry3D.Positions
        LineGeometry3D.Indices = MeshGeometry3D.Indices

        '点オブジェクト作成
        PointGeometry3D = New PointGeometry3D
        PointGeometry3D.Positions = MeshGeometry3D.Positions
        PointGeometry3D.Indices = MeshGeometry3D.Indices

    End Sub

    Private _MeshGeometry3D As MeshGeometry3D
    Private _LineGeometry3D As LineGeometry3D
    Private _PointGeometry3D As PointGeometry3D
    Private _GridGeometry3D As LineGeometry3D
    Private _GridGeometry3DTransform As Media3D.Transform3D

    ''' <summary>
    ''' ソリッドオブジェクト
    ''' </summary>
    ''' <value>
    ''' The mesh geometry3 d.
    ''' </value>
    Public Property MeshGeometry3D As MeshGeometry3D
        Get
            Return _MeshGeometry3D
        End Get
        Set(value As MeshGeometry3D)
            _MeshGeometry3D = value
            NotifyPropertyChanged(NameOf(MeshGeometry3D))
        End Set
    End Property
    ''' <summary>
    ''' ワイヤーオブジェクト
    ''' </summary>
    ''' <value>
    ''' The line geometry3 d.
    ''' </value>
    Public Property LineGeometry3D As LineGeometry3D
        Get
            Return _LineGeometry3D
        End Get
        Set(value As LineGeometry3D)
            _LineGeometry3D = value
            NotifyPropertyChanged(NameOf(LineGeometry3D))
        End Set
    End Property
    ''' <summary>
    ''' 点オブジェクト
    ''' </summary>
    ''' <value>
    ''' The point geometry3 d.
    ''' </value>
    Public Property PointGeometry3D As PointGeometry3D
        Get
            Return _PointGeometry3D
        End Get
        Set(value As PointGeometry3D)
            _PointGeometry3D = value
            NotifyPropertyChanged(NameOf(PointGeometry3D))
        End Set
    End Property
    ''' <summary>
    ''' 座標面グリッド
    ''' </summary>
    ''' <value>
    ''' The grid geometry3 d.
    ''' </value>
    Public Property GridGeometry3D As LineGeometry3D
        Get
            Return _GridGeometry3D
        End Get
        Set(value As LineGeometry3D)
            _GridGeometry3D = value
            NotifyPropertyChanged(NameOf(GridGeometry3D))
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the grid geometry3 d transform.
    ''' </summary>
    ''' <value>
    ''' The grid geometry3 d transform.
    ''' </value>
    Public Property GridGeometry3DTransform As Media3D.Transform3D
        Get
            Return _GridGeometry3DTransform
        End Get
        Set(value As Media3D.Transform3D)
            _GridGeometry3DTransform = value
            NotifyPropertyChanged(NameOf(GridGeometry3DTransform))
        End Set
    End Property
End Class


```

# Windowの作成
完成イメージ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/209026/13997d05-e043-48d6-871d-e8e3fb34080c.png)


まずXAMLのNameSpaceに以下２つを追加します。
xmlns:hx="http://helix-toolkit.org/wpf/SharpDX"
xmlns:sdxm="clr-namespace:SharpDX;assembly=SharpDX.Mathematics" 

SharpDX.MathematicsはSharpDX.Color構造体をXAMLで使用するために追加します。BindingでColorを適用する場合は不要です。また、SharpDXのバージョンによってNamespaceが異なるため、オブジェクトブラウザでColorが属するNamespaceを適宜探してください。

```MainWindowViewModel.vb
<!--
Copyright (c) 2017 Systeman
This software is released under the MIT License, see https://opensource.org/licenses/mit-license.php
-->
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:hx="http://helix-toolkit.org/wpf/SharpDX"
        xmlns:local="clr-namespace:HelixTool_SharpDX"
        xmlns:sdxm="clr-namespace:SharpDX;assembly=SharpDX.Mathematics"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="900">
    <Window.Resources>
        <local:MainWindowViewModel x:Key="MainWindowViewModel" />
    </Window.Resources>
    <Grid DataContext="{StaticResource MainWindowViewModel}">
        <!--Helix.SharpDX本体-->
        <hx:Viewport3DX Panel.ZIndex="100">
            <!--カメラ-->
            <hx:Viewport3DX.Camera>
                <hx:PerspectiveCamera Position="6 6 10" LookDirection="-6 -6 -10" UpDirection="0 1 0" />
            </hx:Viewport3DX.Camera>
          
            <!--光源-->
            <hx:DirectionalLight3D Direction="-5 -4 -3" />

            <!--ソリッドオブジェクト-->
            <hx:MeshGeometryModel3D Geometry="{Binding MeshGeometry3D}" Material="{x:Static hx:PhongMaterials.Orange}" IsRendering="{Binding IsChecked,ElementName=IsDisplaySolidObject}" />
            <!--ワイヤーオブジェクト-->
            <hx:LineGeometryModel3D Geometry="{Binding LineGeometry3D}" Color="{x:Static sdxm:Color.Blue}" IsRendering="{Binding IsChecked,ElementName=IsDisplayWireObject}"/>
            <!--点オブジェクト-->
            <hx:PointGeometryModel3D Geometry="{Binding PointGeometry3D}" Size="4 4" Color="{x:Static sdxm:Color.DeepPink}" IsRendering="{Binding IsChecked,ElementName=IsDisplayPointObject}" />
            <!--座標面グリッド-->
            <hx:LineGeometryModel3D Geometry="{Binding GridGeometry3D}" Transform="{Binding GridGeometry3DTransform}" Thickness="0.5"/>
        </hx:Viewport3DX>
        <!--レンダリングコントロール-->
        <GroupBox Header="表示設定" HorizontalAlignment="Right" VerticalAlignment="Top" Panel.ZIndex="200">
            <StackPanel Orientation="Vertical">
                <CheckBox Name="IsDisplaySolidObject" Content="ソリッド表示" IsChecked="True" />
                <CheckBox Name="IsDisplayWireObject" Content="ワイヤー表示" IsChecked="True" />
                <CheckBox Name="IsDisplayPointObject" Content="点表示" IsChecked="True" />
            </StackPanel>
    
        </GroupBox>
    </Grid>
</Window>

```

# 実行しましょう
表示設定を変更することで、ワイヤーのみの表示や点とワイヤーといった組み合わせで表示が可能です。DirectXの恩恵で、ヌルヌルと動くと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/209026/ef265f09-dce6-6a09-5507-d1d9222c22d5.png)

ワイヤー表示のみだとViewport3DXのRenderTechniqueプロパティにRenderTechniquesManager経由で、Wiresを適用することでも実現可能です。