728x90
반응형
728x170
▶ PanoramaView.xaml
<UserControl x:Class="TestProject.PanoramaView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestProject">
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera
Position="0 0 0"
UpDirection="0 1 0"
LookDirection="0 0 1"
FieldOfView="95" />
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<DirectionalLight
Color="White"
Direction="0 0 1" />
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry="{x:Static local:PanoramaView.Geometry}">
<GeometryModel3D.BackMaterial>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource="{Binding Path=PanoramaImage,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.BackMaterial>
</GeometryModel3D>
</ModelVisual3D.Content>
<ModelVisual3D.Transform>
<Transform3DGroup>
<ScaleTransform3D
ScaleX="1"
ScaleY="1.5"
ScaleZ="1" />
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding Path=RotateX,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Axis="0 1 0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding Path=RotateY,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Axis="1 0 0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
</Transform3DGroup>
</ModelVisual3D.Transform>
</ModelVisual3D>
</Viewport3D>
</UserControl>
728x90
▶ PanoramaView.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace TestProject
{
/// <summary>
/// 파노라마 뷰
/// </summary>
public partial class PanoramaView : UserControl
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 파노라마 이미지 속성 - PanoramaImageProperty
/// <summary>
/// 파노라마 이미지 속성
/// </summary>
public static readonly DependencyProperty PanoramaImageProperty = DependencyProperty.Register
(
"PanoramaImage",
typeof(ImageSource),
typeof(PanoramaView),
new PropertyMetadata(null)
);
#endregion
#region 회전 X 속성 - RotateXProperty
/// <summary>
/// 회전 X 속성
/// </summary>
public static readonly DependencyProperty RotateXProperty = DependencyProperty.Register
(
"RotateX",
typeof(double),
typeof(PanoramaView),
new PropertyMetadata(0.0)
);
#endregion
#region 회전 Y 속성 - RotateXProperty
/// <summary>
/// 회전 Y 속성
/// </summary>
public static readonly DependencyProperty RotateYProperty = DependencyProperty.Register
(
"RotateY",
typeof(double),
typeof(PanoramaView),
new PropertyMetadata(0.0)
);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 지오메트리
/// </summary>
public static Geometry3D Geometry = CreateGeometry();
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 드래그 여부
/// </summary>
private bool isDragging = false;
/// <summary>
/// 시작 포인트
/// </summary>
private Point startPoiint = new Point();
/// <summary>
/// 시작 회전 X
/// </summary>
private double startRotateX = 0.0;
/// <summary>
/// 시작 회전 Y
/// </summary>
private double startRotateY = 0.0;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 파노라마 이미지 - PanoramaImage
/// <summary>
/// 파노라마 이미지
/// </summary>
public ImageSource PanoramaImage
{
get
{
return (ImageSource)GetValue(PanoramaImageProperty);
}
set
{
SetValue(PanoramaImageProperty, value);
}
}
#endregion
#region 회전 X - RotateX
/// <summary>
/// 회전 X
/// </summary>
public double RotateX
{
get
{
return (double)GetValue(RotateXProperty);
}
set
{
SetValue(RotateXProperty, value);
}
}
#endregion
#region 회전 Y - RotateX
/// <summary>
/// 회전 Y
/// </summary>
public double RotateY
{
get
{
return (double)GetValue(RotateYProperty);
}
set
{
SetValue(RotateYProperty, value);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - PanoramaView()
/// <summary>
/// 생성자
/// </summary>
public PanoramaView()
{
InitializeComponent();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 위치 구하기 - GetPosition(theta, y)
/// <summary>
/// 위치 구하기
/// </summary>
/// <param name="theta">쎄타</param>
/// <param name="y">Y</param>
/// <returns>위치</returns>
public static Point3D GetPosition(double theta, double y)
{
double r = Math.Sqrt(1 - y * y);
double x = r * Math.Cos(theta);
double z = r * Math.Sin(theta);
return new Point3D(x, y, z);
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 지오메트리 생성하기 - CreateGeometry()
/// <summary>
/// 지오메트리 생성하기
/// </summary>
/// <returns>지오메트리</returns>
private static Geometry3D CreateGeometry()
{
int divisionTheta = 64;
double maximumTheta = (360.0 / 180.0) * Math.PI;
double deltaTheta = maximumTheta / divisionTheta;
int divitionY = 64;
double minimumY = -1.0;
double maximumY = 1.0;
double deltaY = (maximumY - minimumY) / divitionY;
MeshGeometry3D meshGeometry3D = new MeshGeometry3D();
for(int indexY = 0; indexY <= divitionY; indexY++)
{
double y = minimumY + indexY * deltaY;
for(int indexTheta = 0; indexTheta <= divisionTheta; indexTheta++)
{
double theta = indexTheta * deltaTheta;
meshGeometry3D.Positions.Add(GetPosition(theta, y));
meshGeometry3D.Normals.Add(GetNormalVector(theta, y));
meshGeometry3D.TextureCoordinates.Add(GetTextureCoordinate(theta, y));
}
}
for(int indexY = 0; indexY < divitionY; indexY++)
{
for(int indexTheta = 0; indexTheta < divisionTheta; indexTheta++)
{
int x0 = indexTheta;
int x1 = (indexTheta + 1);
int y0 = indexY * (divisionTheta + 1);
int y1 = (indexY + 1) * (divisionTheta + 1);
meshGeometry3D.TriangleIndices.Add(x0 + y0);
meshGeometry3D.TriangleIndices.Add(x0 + y1);
meshGeometry3D.TriangleIndices.Add(x1 + y0);
meshGeometry3D.TriangleIndices.Add(x1 + y0);
meshGeometry3D.TriangleIndices.Add(x0 + y1);
meshGeometry3D.TriangleIndices.Add(x1 + y1);
}
}
meshGeometry3D.Freeze();
return meshGeometry3D;
}
#endregion
#region 법선 벡터 구하기 - GetNormalVector(double theta, double y)
/// <summary>
/// 법선 벡터 구하기
/// </summary>
/// <param name="theta">쎄타</param>
/// <param name="y">Y</param>
/// <returns>법선 벡터</returns>
private static Vector3D GetNormalVector(double theta, double y)
{
return (Vector3D)GetPosition(theta, y);
}
#endregion
#region 텍스처 좌표 구하기 - GetTextureCoordinate(theta, y)
/// <summary>
/// 텍스처 좌표 구하기
/// </summary>
/// <param name="theta">쎄타</param>
/// <param name="y">Y</param>
/// <returns>텍스처 좌표</returns>
private static Point GetTextureCoordinate(double theta, double y)
{
Matrix matrix = new Matrix();
matrix.Scale(1 / (2 * Math.PI), -0.5);
Point point = new Point(theta, y);
point = point * matrix;
return point;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Protected
#region 마우스 왼쪽 버튼 DOWN 처리하기 - OnMouseLeftButtonDown(e)
/// <summary>
/// 마우스 왼쪽 버튼 DOWN 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
this.isDragging = true;
this.startPoiint = e.GetPosition(this);
this.startRotateX = RotateX;
this.startRotateY = RotateY;
base.OnMouseLeftButtonDown(e);
}
#endregion
#region 마우스 이동시 처리하기 - OnMouseMove(e)
/// <summary>
/// 마우스 이동시 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseMove(MouseEventArgs e)
{
if(this.isDragging == true && e.LeftButton == MouseButtonState.Pressed)
{
Vector deltaVector = this.startPoiint - e.GetPosition(this);
RotateX = this.startRotateX + (deltaVector.X / ActualWidth * 360);
RotateY = this.startRotateY + (deltaVector.Y / ActualHeight * 360);
}
base.OnMouseMove(e);
}
#endregion
#region 마우스 왼쪽 버튼 UP 처리하기 - OnMouseLeftButtonUp(e)
/// <summary>
/// 마우스 왼쪽 버튼 UP 처리하기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
this.isDragging = false;
base.OnMouseLeftButtonUp(e);
}
#endregion
}
}
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestProject"
Width="800"
Height="600"
Title="파노라마 뷰 사용하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Grid Margin="10">
<local:PanoramaView PanoramaImage="IMAGE/sample.jpg" />
</Grid>
</Window>
▶ MainWindow.xaml.cs
using System.Windows;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] DispatcherTimer 클래스 : 사각형을 부드럽게 이동시키기 (0) | 2020.11.15 |
---|---|
[C#/WPF] Control 엘리먼트 : Background 속성에서 null과 Transparent 값 설정시 차이점 비교하기 (0) | 2020.11.06 |
[C#/WPF] LinearGradientBrush 클래스 : 오프셋 값을 사용해 색상 구하기 (0) | 2020.10.28 |
[C#/WPF] 누겟 설치 : FFME.Windows (0) | 2020.10.25 |
[C#/WPF] 커스텀 윈도우 크롬 사용하기 (0) | 2020.10.13 |
[C#/WPF] PolyBezierSegment 클래스 : 1개 이상의 연결되는 베지어 곡선(Quadratic Bezier Curve) 그리기 (0) | 2020.09.23 |
[C#/WPF] PolyQuadraticBezierSegment 클래스 : 1개 이상의 연결되는 2차 베지어 곡선(Quadratic Bezier Curve) 그리기 (0) | 2020.09.23 |
[C#/WPF] StreamGeometry 클래스 : 다각형 그리기 (0) | 2020.09.23 |
[C#/WPF] StreamGeometry 클래스 : 선 그리기 (0) | 2020.09.23 |
[C#/WPF] StreamGeometry 클래스 : 2차 베지어 곡선(Quadratic Bezier Curve) 그리기 (0) | 2020.09.23 |
댓글을 달아 주세요