Как стать автором
Обновить
289.46
TINKOFF
IT’s Tinkoff — просто о сложном

Визуализация простой геометрии в WPF

Время на прочтение 4 мин
Количество просмотров 9.9K

Что такое геометрия модели


Для работы с 3D моделями мы используем специальные конвейеры обработки — OpenGL и DirectX. Когда конвейеры строят картину, они используют информацию:

  • о модели — её материале, геометрии и текстурах,
  • о сцене — освещении и настройке камеры.

Любая модель начинается с геометрии. Геометрия модели — это набор точек в трехмерном пространстве и набор треугольников из этих точек. Треугольник компланарен — он лежит в плоскости, в отличие от фигур с большим числом точек, которые в общем случае не лежат в плоскости.

Если задать у треугольника направление обхода границы, он станет ориентированным треугольником. У ориентированного треугольника в трехмерном пространстве можно выделить внутреннюю и внешнюю стороны. По направлению обхода границы мы также определяем единственный по направлению вектор нормали для каждого треугольника. Так, в геометрию модели, помимо точек и треугольников, входит набор нормалей к каждому из треугольников. При отображении модели точки модели соответствуют точкам, треугольники модели соответствуют граням.

В примере мы сосредоточимся на геометрии модели, остальное будем использовать по мере необходимости. В качестве примера модели вполне подойдет Дельфин ниже:

image

Благодаря технологии WPF мы создаем интерактивные интерфейсы приложений и работаем с 3D-графикой. В примере мы используем стандартные возможности архитектуры WPF: привяжем данные и на их основе разделим модель данных и представление данных (MVVM).

Основной элемент для отображения 3D-содержимого в библиотеке WPF Viewport3D. Например, свойство Camera устанавливает камеру, и мы видим сцену. Второе необходимое свойство Viewport3D Children, коллекция элементов абстрактного типа Visual3D. Конкретная реализация этого класса — класс ModelVisual3D: чтобы его использовать, нужно указать свойство Content абстрактного типа Model3D.

Основные классы для установки свойства Content:

  • GeometryModel3D — отображает одну модель,
  • Light — модель источника света,
  • Model3DGroup — помогает создавать модели с разными материалами.

Необходимые свойства будем устанавливать по привязке.

Модель данных MVVM


В широком смысле любое приложение решает определенную задачу. Модель должна полностью отражать данные в решаемой приложением задаче. Мы упростим пример и исключим нормали — они будут определяться по умолчанию. Нормали важны для отображения текстур или заливки при расчете освещенности.

Подберем основные интерфейсы, которые определяют сущности модели данных MVVM и их связи:

interface IModel3DSet {
        string Description { get; set; }
        ICollection<IModel3D> Models { get; }
    }

interface IModel3D {
        string Description { get; set; }
        ICollection<IPoint3D> Points { get; }
        ICollection<ITriangle3D> Triangles { get; }
    }

interface IPoint3D {
        double X { get; set; }
        double Y { get; set; }
        double Z { get; set; }
        string Coordinates { get; }

        IVector3D DistanceTo(IPoint3D endPoint);
    }

interface ITriangle3D {
        IModel3D Model3D { get; }
        IPoint3D Point1 { get; set; }
        IPoint3D Point2 { get; set; }
        IPoint3D Point3 { get; set; }
    }

interface IVector3D {
        double X { get; set; }
        double Y { get; set; }
        double Z { get; set; }
        double Norm { get; }

        IVector3D Add(IVector3D vector);
        IVector3D Subtract(IVector3D vector);
        IVector3D Multiply(double factor);

        IVector3D CrossProduct(IVector3D vector);
        double DotProduct(IVector3D vector);
    }

Конкретная реализация прямолинейна. Чтобы предупреждать об изменении свойств, используем привычный INotifyPropertyChanged.

ViewModel


В качестве базового класса для ViewModel мы используем:

public abstract class BaseVm<TModel> : Notifier {
        TModel _model;
        public TModel Model {
            get { return _model; }
            set {
                _model = value;
                NotifyWithCallerPropName();
            }
        }
    }

public abstract class BaseVm<TModel, TParentVM> : BaseVm<TModel> {
        public BaseVm(TModel model = default(TModel), TParentVM parentVM = default(TParentVM)) {
            Model = model;
            Parent = parentVM;
        }
        public TParentVM Parent { get; }
    }

Такая структура удобна тем, что позволяет двигаться по иерархии ViewModel в привязках. Классы Vector3D, Triangle3D, Point3D просты, поэтому создавать для них ViewModel не обязательно. Значит нам нужны только два класса ViewModel — Model3DSetVm и Model3DVm.

Представления


Чтобы построить представления, используем подстановку WPF с помощью атрибута DataType="{x:Type local:Type}" при объявлении DataTemplate в словарях ресурсов. В остальном реализация стандартная. Приложение для демонстрации выглядит так:



Что еще нужно знать


  1. В WPF свойство TriangleIndices не обязательное. Если оно не задано, по умолчанию будут созданы треугольники по каждой тройке точек. По этой причине даже при пустом наборе треугольников отображаются грани.

  2. Чтобы создать привязку для коллекции моделей, можно использовать привязку из нашего примера, но подставьте экземпляр Model3DGroup вместо GeometryModel3D. Использовать для этого привязку к свойству Children Viewport3D не получится.

  3. Если мы изменим геометрию или ось вращения, модель перестроится, изображение будет дергаться — анимация начнется заново. Чтобы процесс не прерывался, сохраняйте промежуточные значения модели и применяйте к ним анимацию.

Проект с примером можно найти здесь...

Буду рад, если статья вам в чем-то поможет…
Теги:
Хабы:
+13
Комментарии 8
Комментарии Комментарии 8

Публикации

Информация

Сайт
www.tinkoff.ru
Дата регистрации
Дата основания
Численность
свыше 10 000 человек
Местоположение
Россия