HelixToolkit.Wpf.SharpDXを使用した3Dモデルをソリッド・ワイヤー・プロットを任意の組み合わせで重ねて表示する方法の共有です。
以前はHelixToolkit.Wpfで同じことを実現させようとしていたのですが、メッシュが多くなるとレンダリングが重くなり、満足いくパフォーマンスがでませんでした。そこで、HelixToolkit.Wpf.SharpDXへの移行を模索していたのですが、HelixToolkit.Wpfとではライブラリ構造が異なり、なおかつ英語も含め情報が少なく苦労したのでここに検証の成果を残しておこうと思います。
完成したトライアルプログラムのイメージ
一つのオブジェクトの表示方法をソリッド・ワイヤー・プロットを切り替えられます。ソリッドとワイヤーを同時に表示するといったことも可能です。
検証環境
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でコンバージョンしてみてください。簡単なコードなので、精度よくコンバージョンできるはずです。
nugetでHelixToolkit.Wpf.SharpDXをインストール
パッケージマネージャーコンソールを開き以下コマンドを実行します。
Install-Package HelixToolkit.Wpf.SharpDX
このときは以下の参照が追加されました。
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ファイルがコピーされます。
このファイルを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オブジェクトをそれぞれ作るのは手間です。
'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の作成
まずXAMLのNameSpaceに以下2つを追加します。
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を適宜探してください。
<!--
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の恩恵で、ヌルヌルと動くと思います。
ワイヤー表示のみだとViewport3DXのRenderTechniqueプロパティにRenderTechniquesManager経由で、Wiresを適用することでも実現可能です。