The movement of a model airplane around the globe

It is necessary to drive a three-dimensional model of an airplane in a circle on a 3D model of the Earth. The track is fake, no matter where, the main thing is that it would be in a circle. Approximately as in the image. I read about physics, and about quaternions (also about pitch, roll, yaw), until I really understood, or rather did not enter at all. I have an airplane for example drawn in 3D Max, how to set it exactly on the track according to the coordinates? And how to rotate it? Examples in C# WinForms, where you could peek, especially not found.

enter a description of the image here

Author: Kromster, 2018-10-05

1 answers

There are no tools for working with 3D graphics in Windows Forms. The easiest way to do this is to create a 3D scene using WPF and connect it to Windows Forms using the ElementHost element. In WPF, a three-dimensional model is defined as a XAML MeshGeometry3D element consisting of a set of triangles. For information on converting models from 3D Max to XAML, see: XAML exporter for 3D Studio Max.

An example of a model of the Earth and the texture of the earth's surface can be taken, for example, here.


For example, let's create a WinForms project and add links to WPF assemblies (PresentationCore, PresentationFramework, System.Xaml, WindowsBase).

Adding the Scene file to the project.xaml, which describes a three-dimensional scene: the location of objects and cameras, lighting, rotation parameters, etc.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="black">

   <Grid.Triggers>
        <EventTrigger RoutedEvent="Canvas.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation From="360" To="0" Duration="0:0:15" AutoReverse="False" RepeatBehavior="Forever" Storyboard.TargetName="rotation" Storyboard.TargetProperty="Angle" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Grid.Triggers>

    <Viewport3D x:Name="scene3d">
            <Viewport3D.Camera>
                <PerspectiveCamera FarPlaneDistance="20" FieldOfView="65" 
                                   LookDirection="0,0,-1" NearPlaneDistance="1" 
                                   Position="0,0,4" UpDirection="0,1,0" />
            </Viewport3D.Camera>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup x:Name="modelgroup">
                        <AmbientLight Color="#ffffffff" />
                        <GeometryModel3D x:Name="earthmodel" >                            
                            <GeometryModel3D.Transform>
                                <Transform3DGroup>                                    
                                    <RotateTransform3D>
                                        <RotateTransform3D.Rotation>
                                            <AxisAngleRotation3D Axis="0,1,0" Angle="200" x:Name="rotation" />
                                        </RotateTransform3D.Rotation>
                                    </RotateTransform3D>
                                </Transform3DGroup>
                            </GeometryModel3D.Transform>
                        </GeometryModel3D>
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
    </Viewport3D>
</Grid>

Adding the earth.xaml and files earth.jpg containing the Earth model and texture (XAML is not included in the response text due to size, see the project link below).

Adding the airplane model plane.xaml. I have just a triangle for an example.

<MeshGeometry3D xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Normals="0,0,1 0,0,1 0,0,1" 
Positions="0.1,0,0 0,-0.04,0 0,0.04,0 " 
TriangleIndices="0 1 2" />

For simplicity, let's assume that the plane's path is parallel to a given geographic latitude. In order to not care about the plane's horizontal coordinate (and whether it falls into the frame or not), make it stationary and instead rotate the Earth in the opposite direction, so that the visible result is the same. Our model The Earth is located at the point (0; 0;0), has a radius of 1 and its axis coincides with the Y axis. The Z-axis is directed towards the observer. Then the formulas for calculating the position of the aircraft in the coordinates of the scene will be determined by the following formulas:

X = 0

Y = sin(A)

Z = cos(A)

Where A is the angle corresponding to the geographical latitude, translated into radians. For example, 0.5*Pi for the north pole, 0 for the equator, -0.5*Pi for the south pole poles.

Adding code to initialize the 3D scene and add an airplane to it at a given latitude:

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Diagnostics;
//Ссылки: PresentationCore, PresentationFramework, System.Xaml, WindowsBase

namespace WinForms3D
{
    public static class Earth3D
    {
        //Загружает элемент из XAML-файла по указанному пути
        public static object LoadXaml(string path)
        {
            StreamReader sr = new StreamReader(path);
            using (sr)
            {
                object el = XamlReader.Load(sr.BaseStream);
                return el;
            }
        }

        //Создает трехмерную сцену
        public static FrameworkElement BuildModel(double shirota)
        {
            //Загружаем определение сцены
            FrameworkElement scene = LoadXaml("Scene.xaml") as FrameworkElement;                        
            GeometryModel3D earthmodel = (GeometryModel3D)scene.FindName("earthmodel");
            Model3DGroup modelgroup = (Model3DGroup)scene.FindName("modelgroup");

            //Загружаем модель Земли
            Geometry3D earth = LoadXaml("earth.xaml") as Geometry3D;
            earthmodel.Geometry = earth;

            //Задаем текстуру для модели Земли
            BitmapImage img = new BitmapImage(new Uri("earth.jpg", UriKind.Relative));
            DiffuseMaterial mat = new DiffuseMaterial(new ImageBrush(img));
            earthmodel.Material = mat;

            //Загружаем модель самолета
            Geometry3D g = LoadXaml("plane.xaml") as Geometry3D;
            GeometryModel3D model = new GeometryModel3D();
            model.Geometry = g;
            model.Material = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
            model.BackMaterial = new DiffuseMaterial(new SolidColorBrush(Colors.Red));

            //Задаем позицию самолета на модели
            double alpha = (Math.PI / 180.0) * shirota; //перевод из градусов в радианы
            model.Transform = new TranslateTransform3D(0, Math.Sin(alpha), Math.Cos(alpha) + 0.1);            

            modelgroup.Children.Add(model);
            return scene;
        }
    }
}

In the form, add ElementHost from the section "Interaction with WPF". Let's assign our three-dimensional scene to its Child property:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace WinForms3D
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            elementHost1.Child = Earth3D.BuildModel(30.0);
        }
    }
}

This is what the plane looks like at a parallel of 30 degrees north latitude:

3D Earth

Full archive with the project (VS 2012): https://yadi.sk/d/zxu06TjTMx8bdQ

Sources

WPF - Creating a three-dimensional scene

WPF-Placing a composite WPF control in a Windows Forms form

Loading XAML XML through runtime?

Rotating 3D earth in WPF

 5
Author: MSDN.WhiteKnight, 2018-10-14 18:18:33