WPF で 3D オブジェクトを回転させる

前回の WPF で 3D オブジェクトを表示するに引き続いて、今回は 3D オブジェクトを回転させます。
図のようにボタンを配置して、6 方向の回転ができるように実装します。

次のようにコードを追加・変更します。

<Window x:Class="DiceRotationWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:local="clr-namespace:DiceRotationWpf"
Title="Dice Rotation" Height="600" Width="900">
<Window.Resources>
<Style x:Key="FaceStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="30"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="FontSize" Value="20"/>
</Style>
<Style x:Key="RepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Width" Value="50"/>
<Setter Property="Height" Value="50"/>
<Setter Property="FontSize" Value="32"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid x:Name="DicePanel" Background="#FF333333">
<Grid Visibility="Hidden">
<TextBlock x:Name="Face1" Style="{DynamicResource FaceStyle}" Text="1" Background="#FF222222"/>
<TextBlock x:Name="Face2" Style="{DynamicResource FaceStyle}" Text="2" Background="#FFDF2C2C"/>
<TextBlock x:Name="Face3" Style="{DynamicResource FaceStyle}" Text="3" Background="#FFEE9319"/>
<TextBlock x:Name="Face4" Style="{DynamicResource FaceStyle}" Text="4" Background="#FFE3E60A"/>
<TextBlock x:Name="Face5" Style="{DynamicResource FaceStyle}" Text="5" Background="#FF29D214"/>
<TextBlock x:Name="Face6" Style="{DynamicResource FaceStyle}" Text="6" Background="#FF4444BB"/>
</Grid>
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,10"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Transform>
<Transform3DGroup>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis="-0.8,0.3,0.5" Angle="60"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<MatrixTransform3D x:Name="matrixTransform"/>
</Transform3DGroup>
</ModelVisual3D.Transform>
<!-- ModelVisual3D.Content は省略 -->
</ModelVisual3D>
</Viewport3D>
</Grid>
<Grid Background="#FFF8F8F8" Grid.Column="1" Width="300">
<Canvas Height="230" Width="230">
<RepeatButton Content="↑" CommandParameter="-x" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="90" Canvas.Top="20" Click="Rotate_Click"/>
<RepeatButton Content="↓" CommandParameter="+x" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="90" Canvas.Top="160" Click="Rotate_Click"/>
<RepeatButton Content="←" CommandParameter="-y" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="20" Canvas.Top="90" Click="Rotate_Click"/>
<RepeatButton Content="→" CommandParameter="+y" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="160" Canvas.Top="90" Click="Rotate_Click"/>
<RepeatButton Content="↘" CommandParameter="-z" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="160" Canvas.Top="20" Click="Rotate_Click"/>
<RepeatButton Content="↙" CommandParameter="+z" Style="{DynamicResource RepeatButtonStyle}" Canvas.Left="20" Canvas.Top="20" Click="Rotate_Click"/>
</Canvas>
</Grid>
</Grid>
</Window>
view raw MainWindow.xaml hosted with ❤ by GitHub
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Media3D;
namespace DiceRotationWpf
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
const double AngleDelta = 5.0;
static readonly Dictionary<string, Vector3D> Axes = new Dictionary<string, Vector3D>
{
{ "-x", Vector3D.Parse("-1,0,0") },
{ "+x", Vector3D.Parse("1,0,0") },
{ "-y", Vector3D.Parse("0,-1,0") },
{ "+y", Vector3D.Parse("0,1,0") },
{ "-z", Vector3D.Parse("0,0,-1") },
{ "+z", Vector3D.Parse("0,0,1") },
};
void Rotate_Click(object sender, RoutedEventArgs e)
{
var button = (RepeatButton)sender;
var command = (string)button.CommandParameter;
matrixTransform.Rotate(Axes[command], AngleDelta);
}
}
public static class Media3DUtility
{
public static void Rotate(this MatrixTransform3D transform, Vector3D axis, double angle)
{
var matrix = transform.Matrix;
matrix.Rotate(new Quaternion(axis, angle));
transform.Matrix = matrix;
}
}
}

ボタンとして RepeatButton を配置しています。
RepeatButton は、押したままにしておけば断続的に Click イベントが発生します。
また回転の状態を表すために、ModelVisual3D.Transform の中で MatrixTransform3D を使います。

回転には、回転軸と回転角度が必要です。

  • 回転軸は、ベクトルで表されます。
  • 回転角度は、回転軸の方向に右ねじを押し込む場合を正とします。

6 つのボタンはそれぞれ、x, y, z 軸を回転軸とした正方向または負方向の回転を表します。
1 回の Click イベントにつき 5° ずつ回転させています。
なお、カメラは z 軸上の正の位置から原点方向を見下ろし、右側が x 軸の正、上側が y 軸の正を表しています。

Click イベントハンドラーの中で、Matrix3D.Rotate メソッドを呼び出すことでオブジェクトを回転させます。
引数には、回転軸と回転角度を表す Quaternion (四元数) を指定します。
このように、回転軸と回転角度がわかっている場合は比較的簡単に実装ができます。

下図は、最初の状態から x 軸のまわりに -60° 回転させたところです。

全体のソースコードは DiceRotationWpf (GitHub) にあります。
マウスまたはタッチのドラッグ操作でも回転できるようになっています。

Dice Rotation

 

前回: WPF で 3D オブジェクトを表示する
次回: 3D における回転の表現と相互変換

作成したサンプル
DiceRotationWpf (GitHub)

バージョン情報
.NET Framework 4.5

参照
3-D グラフィックスの概要
3-D 変換の概要

2件のフィードバック to “WPF で 3D オブジェクトを回転させる”

  1. WPF で 3D オブジェクトを表示する | Do Design Space Says:

    […] 次回: WPF で 3D オブジェクトを回転させる […]

  2. 3D における回転の表現と相互変換 | Do Design Space Says:

    […] WPF で 3D オブジェクトを回転させるではオブジェクトの回転の状態を行列で表していましたが、 3 […]


コメントを残す