MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Движение модели самолета вокруг Земного шара"
Answer 893114
В Windows Forms нет никаких средств для работы с 3D-графикой. Самый простой способ сделать это - создать 3D-сцену средствами WPF и подключить ее к Windows Forms с помощью элемента ElementHost. В WPF трехмерная модель задается как элемент XAML MeshGeometry3D, состоящий из набора треугольников. По конвертации моделей из 3D Max в XAML см. здесь: XAML exporter for 3D Studio Max.
Пример модели Земли и текстуры земной поверхности можно взять, например, здесь.
Для примера, создадим проект WinForms и добавим в него ссылки на сборки WPF (PresentationCore, PresentationFramework, System.Xaml, WindowsBase).
Добавим в проект файл Scene.xaml, описывающий трехмерную сцену: расположение объектов и камеры, освещение, параметры вращения и т.п.
<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>
Добавим файлы earth.xaml и earth.jpg, содержащие модель и текстуру Земли (XAML не включен в текст ответа из-за размера, см. ссылку на проект ниже).
Добавим модель самолета plane.xaml. У меня для примера просто треугольник.
<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" />
Для простоты предположим, что путь самолета является параллелью заданной географической широты. Чтобы не заботится о горизонтальной координате самолета (и о том, попадает ли он в кадр или нет), сделаем его неподвижным и вместо этого будем вращать Землю в противоположном направлении, так что видимый результат будет тем же самым. Наша модель Земли располагается в точке (0;0;0), имеет радиус 1 и ее ось совпадает с осью Y. Ось Z направлена в сторону наблюдателя. Тогда формулы для расчета положения самолета в координатах сцены будут определяться следующими формулами:
x = 0
y = sin(A)
z = cos(A)
где А - угол, соответствующий географической широте, переведенный в радианы. Например, 0.5*Pi для северного полюса, 0 для экватора, -0.5*Pi для южного полюса.
Добавим код для инициализации 3D-сцены и добавления в нее самолета на заданной широте:
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; } } }
В форму добавим ElementHost из раздела "Взаимодействие с WPF". Его свойству Child присвоим нашу трехмерную сцену:
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); } } }
Так выглядит самолет на параллели в 30 градусов северной широты:
Полный архив с проектом (VS 2012): https://yadi.sk/d/zxu06TjTMx8bdQ
Источники
WPF - Создание трехмерной сцены
WPF - Размещение составного элемента управления WPF в форме Windows Forms
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.