첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

728x90
반응형

TestProject.zip
다운로드

▶ 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"
    Width="800"
    Height="600"
    Title="3차원 지구본 사용하기"
    Loaded="Window_Loaded"
    KeyDown="Window_KeyDown">
    <Grid>
        <Viewport3D Name="viewport3D" />
    </Grid>
</Window>

 

▶ MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 모델 그룹
        /// </summary>
        private Model3DGroup modelGroup = new Model3DGroup();

        /// <summary>
        /// 카메라
        /// </summary>
        private PerspectiveCamera camera;

        /// <summary>
        /// 카메라 파이
        /// </summary>
        private double cameraPhi = Math.PI / 6.0;

        /// <summary>
        /// 카메라 세타
        /// </summary>
        private double cameraTheta = Math.PI / 6.0;

        /// <summary>
        /// 카메라 R
        /// </summary>
        private double cameraR = 3.0;

        /// <summary>
        /// 카메라 델타 파이
        /// </summary>
        private const double CAMERA_DELTA_PHI = 0.1;

        /// <summary>
        /// 카메라 델타 세타
        /// </summary>
        private const double CAMERA_DELTA_THETA = 0.1;

        /// <summary>
        /// 카메라 델타 R
        /// </summary>
        private const double CAMERA_DELTA_R = 0.1;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)

        /// <summary>
        /// 윈도우 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.camera = new PerspectiveCamera();

            this.camera.FieldOfView = 60;

            this.viewport3D.Camera = this.camera;

            SetCameraPosition();

            DefineLight();

            DefineModel(this.modelGroup);

            ModelVisual3D modelVisual = new ModelVisual3D();

            modelVisual.Content = this.modelGroup;

            this.viewport3D.Children.Add(modelVisual);
        }

        #endregion
        #region 윈도우 키 DOWN 처리하기 - Window_KeyDown(sender, e)

        /// <summary>
        /// 윈도우 키 DOWN 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            switch(e.Key)
            {
                case Key.Up :

                    this.cameraPhi += CAMERA_DELTA_PHI;

                    if(this.cameraPhi > Math.PI / 2.0)
                    {
                        this.cameraPhi = Math.PI / 2.0;
                    }

                    break;

                case Key.Down :

                    this.cameraPhi -= CAMERA_DELTA_PHI;

                    if(this.cameraPhi < -Math.PI / 2.0)
                    {
                        this.cameraPhi = -Math.PI / 2.0;
                    }

                    break;

                case Key.Left :

                    this.cameraTheta += CAMERA_DELTA_THETA;

                    break;

                case Key.Right :

                    this.cameraTheta -= CAMERA_DELTA_THETA;

                    break;

                case Key.Add     :
                case Key.OemPlus :

                    this.cameraR -= CAMERA_DELTA_R;

                    if(this.cameraR < CAMERA_DELTA_R)
                    {
                        this.cameraR = CAMERA_DELTA_R;
                    }

                    break;

                case Key.Subtract :
                case Key.OemMinus :

                    this.cameraR += CAMERA_DELTA_R;

                    break;
            }

            SetCameraPosition();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 카메라 위치 설정하기 - SetCameraPosition()

        /// <summary>
        /// 카메라 위치 설정하기
        /// </summary>
        private void SetCameraPosition()
        {
            double y   = this.cameraR * Math.Sin(this.cameraPhi);
            double hyp = this.cameraR * Math.Cos(this.cameraPhi);
            double x   = hyp * Math.Cos(this.cameraTheta);
            double z   = hyp * Math.Sin(this.cameraTheta);

            this.camera.Position = new Point3D(x, y, z);

            this.camera.LookDirection = new Vector3D(-x, -y, -z);

            this.camera.UpDirection = new Vector3D(0, 1, 0);
        }

        #endregion
        #region 조명 정의하기 - DefineLight()

        /// <summary>
        /// 조명 정의하기
        /// </summary>
        private void DefineLight()
        {
            AmbientLight ambientLight = new AmbientLight(Colors.Gray);

            DirectionalLight directionalLight = new DirectionalLight(Colors.Gray, new Vector3D(-1.0, -3.0, -2.0));

            this.modelGroup.Children.Add(ambientLight);

            this.modelGroup.Children.Add(directionalLight);
        }

        #endregion
        #region 구체 생성하기 - CreateSphere(modelGroup, sphereMesh, sphereMaterial, radius, centerX, centerY, centerZ, phiCount, thetaCount)

        /// <summary>
        /// 구체 생성하기
        /// </summary>
        /// <param name="modelGroup">모델 그룹</param>
        /// <param name="sphereMesh">구체 메시</param>
        /// <param name="sphereMaterial">구체 재료</param>
        /// <param name="radius">반지름</param>
        /// <param name="centerX">중심 X 좌표</param>
        /// <param name="centerY">중심 Y 좌표</param>
        /// <param name="centerZ">중심 Z 좌표</param>
        /// <param name="phiCount">파이 카운트</param>
        /// <param name="thetaCount">세타 카운트</param>
        private void CreateSphere(Model3DGroup modelGroup, ref MeshGeometry3D sphereMesh, Material sphereMaterial, double radius,
            double centerX, double centerY, double centerZ, int phiCount, int thetaCount)
        {
            if(sphereMesh == null)
            {
                sphereMesh = new MeshGeometry3D();

                GeometryModel3D newModel = new GeometryModel3D(sphereMesh, sphereMaterial);

                modelGroup.Children.Add(newModel);
            }

            double deltaPhi   = Math.PI / phiCount;
            double deltaTheta = 2 * Math.PI / thetaCount;

            int positionCount = sphereMesh.Positions.Count;

            double halfPI = Math.PI / 2;

            for(int p = 0; p <= phiCount; p++)
            {
                double r1 = radius * Math.Cos(halfPI);
                double y1 = radius * Math.Sin(halfPI);

                double theta = 0;

                for(int t = 0; t <= thetaCount; t++)
                {
                    sphereMesh.Positions.Add
                    (
                        new Point3D
                        (
                            centerX +  r1 * Math.Cos(theta),
                            centerY +  y1,
                            centerZ + -r1 * Math.Sin(theta)
                        )
                    );

                    sphereMesh.TextureCoordinates.Add
                    (
                        new Point
                        (
                            (double)t / thetaCount,
                            (double)p / phiCount
                        )
                    );

                    theta += deltaTheta;
                }

                halfPI -= deltaPhi;
            }

            int i1;
            int i2;
            int i3;
            int i4;

            for(int p = 0; p <= phiCount - 1; p++)
            {
                i1 = p  * (thetaCount + 1);
                i2 = i1 + (thetaCount + 1);

                for(int t = 0; t <= thetaCount - 1; t++)
                {
                    i3 = i1 + 1;
                    i4 = i2 + 1;

                    sphereMesh.TriangleIndices.Add(positionCount + i1);
                    sphereMesh.TriangleIndices.Add(positionCount + i2);
                    sphereMesh.TriangleIndices.Add(positionCount + i4);

                    sphereMesh.TriangleIndices.Add(positionCount + i1);
                    sphereMesh.TriangleIndices.Add(positionCount + i4);
                    sphereMesh.TriangleIndices.Add(positionCount + i3);

                    i1 += 1;
                    i2 += 1;
                }
            }
        }

        #endregion
        #region 모델 정의하기 - DefineModel(modelGroup)

        /// <summary>
        /// 모델 정의하기
        /// </summary>
        /// <param name="modelGroup">모델 그룹</param>
        private void DefineModel(Model3DGroup modelGroup)
        {
            Model3DGroup globeModel = new Model3DGroup();

            modelGroup.Children.Add(globeModel);

            ImageBrush globeBrush = new ImageBrush(new BitmapImage(new Uri("pack://application:,,,/world.jpg")));

            Material globeMaterial = new DiffuseMaterial(globeBrush);

            MeshGeometry3D globeMesh = null;

            CreateSphere(globeModel, ref globeMesh, globeMaterial, 1, 0, 0, 0, 100, 150);
        }

        #endregion
    }
}
728x90
반응형
Posted by 사용자 icodebroker
TAG , , ,

댓글을 달아 주세요