첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ 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>

 

▶ 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
반응형
그리드형
Posted by 사용자 icodebroker
TAG , , ,

댓글을 달아 주세요