今回は WPF の 3D 機能を使って、Leap Motion のサンプルアプリケーションをつくります。
WPF の 3D 機能を使うにあたっては、3DTools というモジュールが Microsoft より公開されていますので、以下のサイトからダウンロードしておきましょう。
ちなみに、3DTools はソースコードも公開されていますので、プロジェクトという形で追加することもでき、好きなようにカスタマイズして利用することもできます。
http:/
ダウンロードしたら 3DTools に含まれる 3DTools.dll も参照の追加を行っておきます。
今回は 3DTools に含まれる TrackballDecorator クラスと ScreenSpaceLines3D クラスを使います。
TrackballDecorator クラスは Viewport3D クラスの外側に置くだけで、マウス操作でカメラ位置を操作することができるようになります。
また、WPF の 3D には単に線を引くという機能がありませんが、ScreenSpaceLines3D クラスでそれが可能になります。
Leap Motion の座標系は右手系と呼ばれる座標系になっており、右がX軸、上がY軸、手前がZ軸の正方向です。WPF の 3D 空間も同じく右手系となっていますので、座標系について特別な変換は必要ありません。
Leap Motion で認識できるエリアは、Leap Motion デバイスの上に、以下のような形で広がっています。逆ピラミッド型で、Z軸方向が少し狭くなっています。
上図で、内側にある直方体は InteractionBox と呼びます。
一般的なアプリケーションでは逆ピラミッド型での座標より、直方体での座標のほうが扱いやすいことから InteractionBox という概念が用意されており、相互に変換を行うことができるようになっています。
今回はとりあえず、座標軸と、Leap Motion が認識する逆ピラミッド型のエリアを作ってみます。
MainWindows.xaml.cs を以下のように書き換えます。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; using System.Windows.Navigation; using System.Windows.Shapes; namespace LeapSample { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private Leap.Controller controller = new Leap.Controller(); private ModelVisual3D trackingAreaModel = null; public MainWindow() { InitializeComponent(); var root = this.Content as Grid; root.Children.Add(new TextBlock()); Init(); CompositionTarget.Rendering += CompositionTarget_Rendering; } private void CompositionTarget_Rendering(object sender, EventArgs e) { var root = this.Content as Grid; var textBlock = root.Children[0] as TextBlock; Leap.Frame frame = controller.Frame(); string msg = string.Format("IsValid={0}\n", frame.IsValid); msg += string.Format("Id={0}\n", frame.Id); msg += string.Format("Timestamp={0}\n", frame.Timestamp); msg += string.Format("InteractionBox={0}\n", frame.InteractionBox); msg += string.Format("CurrentFramesPerSecond={0}\n", frame.CurrentFramesPerSecond); msg += string.Format("Hands.Count={0}\n", frame.Hands.Count); msg += string.Format("Pointables.Count={0}\n", frame.Pointables.Count); msg += string.Format("Fingers.Count={0}\n", frame.Fingers.Count); msg += string.Format("Tools.Count={0}\n", frame.Tools.Count); textBlock.Text = msg; var td = root.Children[1] as _3DTools.TrackballDecorator; var viewport = td.Content as Viewport3D; if (frame.IsValid) { if (trackingAreaModel == null) { trackingAreaModel = CreateTrackingAreaModel(frame.InteractionBox); viewport.Children.Add(trackingAreaModel); } } else { viewport.Children.Remove(trackingAreaModel); trackingAreaModel = null; } } private void Init() { var root = this.Content as Grid; // trackball var td = new _3DTools.TrackballDecorator(); root.Children.Add(td); // viewport var viewport = new Viewport3D(); td.Content = viewport; // camera var camera = new PerspectiveCamera(); camera.Position = new Point3D(0, 1000, 1200); camera.UpDirection = new Vector3D(0, 1, 0); camera.LookDirection = (new Point3D(0, 100, 0) - camera.Position); viewport.Camera = camera; // light var model = new ModelVisual3D(); var light = new DirectionalLight { Color = Colors.White, Direction = new Vector3D(-1, -1, -1) }; model.Content = light; viewport.Children.Add(model); // axis viewport.Children.Add(CreateAxisModel()); } private ModelVisual3D CreateAxisModel() { var xAxis = new _3DTools.ScreenSpaceLines3D(); xAxis.Points.Add(new Point3D(-2000, 0, 0)); xAxis.Points.Add(new Point3D(2000, 0, 0)); xAxis.Color = Colors.Red; xAxis.Thickness = 1; var yAxis = new _3DTools.ScreenSpaceLines3D(); yAxis.Points.Add(new Point3D(0, -2000, 0)); yAxis.Points.Add(new Point3D(0, 2000, 0)); yAxis.Color = Colors.Green; yAxis.Thickness = 1; var zAxis = new _3DTools.ScreenSpaceLines3D(); zAxis.Points.Add(new Point3D(0, 0, -2000)); zAxis.Points.Add(new Point3D(0, 0, 2000)); zAxis.Color = Colors.Blue; zAxis.Thickness = 1; var axis = new ModelVisual3D(); axis.Children.Add(xAxis); axis.Children.Add(yAxis); axis.Children.Add(zAxis); return axis; } private ModelVisual3D CreateTrackingAreaModel(Leap.InteractionBox ib) { double k = (ib.Center.y + ib.Height / 2) / (ib.Center.y - ib.Height / 2); var lb = new Point3D((ib.Center.x - ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z - ib.Depth / 2) * k); // left back var rb = new Point3D((ib.Center.x + ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z - ib.Depth / 2) * k); // right back var rf = new Point3D((ib.Center.x + ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z + ib.Depth / 2) * k); // right front var lf = new Point3D((ib.Center.x - ib.Width / 2) * k, ib.Center.y + ib.Height / 2, (ib.Center.z + ib.Depth / 2) * k); // left front var origin = new Point3D(0, 0, 0); var m = new _3DTools.ScreenSpaceLines3D(); m.Points.Add(lb); m.Points.Add(rb); m.Points.Add(rb); m.Points.Add(rf); m.Points.Add(rf); m.Points.Add(lf); m.Points.Add(lf); m.Points.Add(lb); m.Points.Add(origin); m.Points.Add(lb); m.Points.Add(origin); m.Points.Add(rb); m.Points.Add(origin); m.Points.Add(rf); m.Points.Add(origin); m.Points.Add(lf); m.Color = Colors.SlateGray; return m; } } }
実行結果は以下となります。
マウスを左クリックしながら動かすとカメラ位置を移動できます。右クリックしながら動かすと拡大縮小となります。
今回はここまでです。