using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace _3D
{
    public partial class MainWindow : Window
    {
        Point3D camOrgPos;
        double camOrgFieldOfView;

        public MainWindow()
        {
            InitializeComponent();


            /*** Die Objekte *******************************************/

            // Die Pyramide
            MeshGeometry3D pyramidMesh = new MeshGeometry3D();

            // Eckpunkte der Dreiecke 
            Point3DCollection pyramidPoints = new Point3DCollection();
            pyramidPoints.Add(new Point3D(0, 1.2, 0));
            pyramidPoints.Add(new Point3D(1, 0, -1));
            pyramidPoints.Add(new Point3D(-1, 0, -1));

            pyramidPoints.Add(new Point3D(0, 1.2, 0));
            pyramidPoints.Add(new Point3D(-1, 0, -1));
            pyramidPoints.Add(new Point3D(-1, 0, 1));

            pyramidPoints.Add(new Point3D(0, 1.2, 0));
            pyramidPoints.Add(new Point3D(-1, 0, 1));
            pyramidPoints.Add(new Point3D(1, 0, 1));

            pyramidPoints.Add(new Point3D(0, 1.2, 0));
            pyramidPoints.Add(new Point3D(1, 0, 1));
            pyramidPoints.Add(new Point3D(1, 0, -1));

            // Bodenplatte
            pyramidPoints.Add(new Point3D(1, 0, -1));
            pyramidPoints.Add(new Point3D(1, 0, 1));
            pyramidPoints.Add(new Point3D(-1, 0, 1));

            pyramidPoints.Add(new Point3D(-1, 0, 1));
            pyramidPoints.Add(new Point3D(-1, 0, -1));
            pyramidPoints.Add(new Point3D(1, 0, -1));

            pyramidMesh.Positions = pyramidPoints;


            // Dreiecke vorgeben
            Int32Collection pyramidTriangleIndices = new Int32Collection();
            pyramidTriangleIndices.Add(0);
            pyramidTriangleIndices.Add(1);
            pyramidTriangleIndices.Add(2);

            pyramidTriangleIndices.Add(3);
            pyramidTriangleIndices.Add(4);
            pyramidTriangleIndices.Add(5);

            pyramidTriangleIndices.Add(6);
            pyramidTriangleIndices.Add(7);
            pyramidTriangleIndices.Add(8);

            pyramidTriangleIndices.Add(9);
            pyramidTriangleIndices.Add(10);
            pyramidTriangleIndices.Add(11);

            // Bodenplatte
            pyramidTriangleIndices.Add(12);
            pyramidTriangleIndices.Add(13);
            pyramidTriangleIndices.Add(14);
            pyramidTriangleIndices.Add(15);
            pyramidTriangleIndices.Add(16);
            pyramidTriangleIndices.Add(17);

            pyramidMesh.TriangleIndices = pyramidTriangleIndices;

            // Pyramide zuammenbauen aus Modell und Material
            GeometryModel3D pyramid = new GeometryModel3D();
            pyramid.Geometry = pyramidMesh;
            pyramid.Material = new DiffuseMaterial(Brushes.Aquamarine);


            // Weg vor Pyramide
            MeshGeometry3D pathZMesh = new MeshGeometry3D();
            Point3DCollection pathZPoints = new Point3DCollection();
            pathZPoints.Add(new Point3D(0.25, 0.001, 4));
            pathZPoints.Add(new Point3D(0.25, 0.001, 1.25));
            pathZPoints.Add(new Point3D(-0.25, 0.001, 1.25));
            pathZPoints.Add(new Point3D(-0.25, 0.001, 4));
            pathZMesh.Positions = pathZPoints;
            Int32Collection pathZTriangleIndices = new Int32Collection();
            pathZTriangleIndices.Add(0);
            pathZTriangleIndices.Add(1);
            pathZTriangleIndices.Add(2);
            pathZTriangleIndices.Add(2);
            pathZTriangleIndices.Add(3);
            pathZTriangleIndices.Add(0);
            pathZMesh.TriangleIndices = pathZTriangleIndices;
            GeometryModel3D pathZ = new GeometryModel3D();
            pathZ.Geometry = pathZMesh;
            pathZ.Material = new DiffuseMaterial(Brushes.White);

            // Weg rechts von Pyramide
            MeshGeometry3D pathXMesh = new MeshGeometry3D();
            Point3DCollection pathXPoints = new Point3DCollection();
            pathXPoints.Add(new Point3D(1.25, 0.001, 0.25));
            pathXPoints.Add(new Point3D(4, 0.001, 0.25));
            pathXPoints.Add(new Point3D(4, 0.001, -0.25));
            pathXPoints.Add(new Point3D(1.25, 0.001, -0.25));
            pathXMesh.Positions = pathXPoints;
            Int32Collection pathXTriangleIndices = new Int32Collection();
            pathXTriangleIndices.Add(0);
            pathXTriangleIndices.Add(1);
            pathXTriangleIndices.Add(2);
            pathXTriangleIndices.Add(2);
            pathXTriangleIndices.Add(3);
            pathXTriangleIndices.Add(0);
            pathXMesh.TriangleIndices = pathXTriangleIndices;
            GeometryModel3D pathX = new GeometryModel3D();
            pathX.Geometry = pathXMesh;
            pathX.Material = new DiffuseMaterial(Brushes.White);


            MeshGeometry3D groundMesh = new MeshGeometry3D();
            Point3DCollection groundPoints = new Point3DCollection();
            groundPoints.Add(new Point3D(50, 0, 50));
            groundPoints.Add(new Point3D(50, 0, -50));
            groundPoints.Add(new Point3D(-50, 0, -50));
            groundPoints.Add(new Point3D(-50, 0, 50));
            groundMesh.Positions = groundPoints;
            Int32Collection groundTriangleIndices = new Int32Collection();
            groundTriangleIndices.Add(0);
            groundTriangleIndices.Add(1);
            groundTriangleIndices.Add(2);
            groundTriangleIndices.Add(2);
            groundTriangleIndices.Add(3);
            groundTriangleIndices.Add(0);
            groundMesh.TriangleIndices = groundTriangleIndices;
            GeometryModel3D ground = new GeometryModel3D();
            ground.Geometry = groundMesh;
            ground.Material = new DiffuseMaterial(Brushes.Brown);



            /*** Die Szenerie ******************************************/


            // Körper und Lichtquellen der Szenerie in eine Modell-Gruppe einfügen
            // anschließend Modell-Gruppe in ein ModelVisual3D-Objekt aufnehmen
            Model3DGroup modelGroup = new Model3DGroup();

            // Die Körper
            modelGroup.Children.Add(ground);
            modelGroup.Children.Add(pyramid);
            modelGroup.Children.Add(pathZ);
            modelGroup.Children.Add(pathX);

            // Lichtquellen
            AmbientLight ambientLight = new AmbientLight(Colors.DarkGray);
            modelGroup.Children.Add(ambientLight);

            DirectionalLight directionalLight = new DirectionalLight();
            directionalLight.Color = Colors.White;
            directionalLight.Direction = new Vector3D(5, 0, 0);
            modelGroup.Children.Add(directionalLight);


            ModelVisual3D modelVisual3D = new ModelVisual3D();
            modelVisual3D.Content = modelGroup;


            // 3D-Modell und Kamera in den Viewport einfügen
            // Das Viewport-Objekt myViewport3D wird im XAML-Code erzeugt

            PerspectiveCamera myPCamera = new PerspectiveCamera();
            camOrgPos = new Point3D(0, 0.5, 5);
            myPCamera.Position = camOrgPos;
            camOrgFieldOfView = 60;
            myPCamera.FieldOfView = camOrgFieldOfView;
            myPCamera.LookDirection = new Vector3D(0, -0.5, -5);
            myPCamera.UpDirection = new Vector3D(0, 1, 0);
            myViewport3D.Camera = myPCamera;

            myViewport3D.Children.Add(modelVisual3D);
        }


        private void sliderH_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            PerspectiveCamera cam = (PerspectiveCamera)myViewport3D.Camera;
            try
            {
                double camDist = Math.Sqrt(Math.Pow(camOrgPos.X, 2) + Math.Pow(camOrgPos.Z, 2));
                cam.Position = new Point3D(camDist * (Math.Sin((Math.PI * sliderH.Value) / 180)), 
                                           camOrgPos.Y,                                           
                                           camDist * (Math.Cos((Math.PI * sliderH.Value) / 180)) );

                cam.LookDirection = new Vector3D(-cam.Position.X,
                                                 -cam.Position.Y,
                                                 -cam.Position.Z);

            }
            catch (Exception) { }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            PerspectiveCamera cam = (PerspectiveCamera)myViewport3D.Camera;
            cam.Position = camOrgPos;
            cam.LookDirection = new Vector3D(-cam.Position.X,
                                             -cam.Position.Y,
                                             -cam.Position.Z);
            cam.FieldOfView = camOrgFieldOfView;
            sliderH.Value = 0;
            sliderV.Value = 60;
        }

        private void sliderV_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            PerspectiveCamera cam = (PerspectiveCamera)myViewport3D.Camera;
            try
            {
                cam.FieldOfView = sliderV.Value;
            }
            catch (Exception) { }
        }

    }
}
