Что такое геометрия модели
Для работы с 3D моделями мы используем специальные конвейеры обработки — OpenGL и DirectX. Когда конвейеры строят картину, они используют информацию:
- о модели — её материале, геометрии и текстурах,
- о сцене — освещении и настройке камеры.
Любая модель начинается с геометрии. Геометрия модели — это набор точек в трехмерном пространстве и набор треугольников из этих точек. Треугольник компланарен — он лежит в плоскости, в отличие от фигур с большим числом точек, которые в общем случае не лежат в плоскости.
Если задать у треугольника направление обхода границы, он станет ориентированным треугольником. У ориентированного треугольника в трехмерном пространстве можно выделить внутреннюю и внешнюю стороны. По направлению обхода границы мы также определяем единственный по направлению вектор нормали для каждого треугольника. Так, в геометрию модели, помимо точек и треугольников, входит набор нормалей к каждому из треугольников. При отображении модели точки модели соответствуют точкам, треугольники модели соответствуют граням.
В примере мы сосредоточимся на геометрии модели, остальное будем использовать по мере необходимости. В качестве примера модели вполне подойдет Дельфин ниже:
Благодаря технологии 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 в словарях ресурсов. В остальном реализация стандартная. Приложение для демонстрации выглядит так:
Что еще нужно знать
- В WPF свойство TriangleIndices не обязательное. Если оно не задано, по умолчанию будут созданы треугольники по каждой тройке точек. По этой причине даже при пустом наборе треугольников отображаются грани.
- Чтобы создать привязку для коллекции моделей, можно использовать привязку из нашего примера, но подставьте экземпляр Model3DGroup вместо GeometryModel3D. Использовать для этого привязку к свойству Children Viewport3D не получится.
- Если мы изменим геометрию или ось вращения, модель перестроится, изображение будет дергаться — анимация начнется заново. Чтобы процесс не прерывался, сохраняйте промежуточные значения модели и применяйте к ним анимацию.
Проект с примером можно найти здесь...
Буду рад, если статья вам в чем-то поможет…
Автор: Tinkoff.ru