Visualization of simple geometry in WPF

What is model geometry?


To work with 3D models, we use special processing pipelines - OpenGL and DirectX . When conveyors build a picture, they use information:

  • about the model - its material, geometry and textures,
  • about the scene - lighting and camera settings.

Any model begins with geometry. The geometry of the model is a set of points in three-dimensional space and a set of triangles from these points. A triangle is coplanar - it lies in a plane, in contrast to figures with a large number of points, which generally do not lie in a plane.

If you set the triangle to go around the border, it will become an oriented triangle. In an oriented triangle in three-dimensional space, the inner and outer sides can be distinguished. In the direction of going around the border, we also determine the only normal direction vector for each triangle. So, in addition to points and triangles, the model geometry includes a set of normals for each of the triangles. When displaying the model, the model points correspond to the points, the model triangles correspond to the faces.

In the example, we will focus on the geometry of the model, the rest will be used as necessary. As an example of the model, the Dolphin below is quite suitable:

image

Thanks to the WPF technology, we create interactive application interfaces and work with 3D graphics. In the example, we use the standard features of the WPF architecture : bind the data and based on it we will separate the data model and data representation ( MVVM ).

The main element for displaying 3D content in the WPF library is Viewport3D . For example, the Camera property sets the camera, and we see the scene. The second required property of Viewport3D is Children, a collection of elements of the abstract type Visual3D . The concrete implementation of this class is the ModelVisual3D class : to use it, you need to specify the Content property of the abstract type Model3D .

The main classes for setting the Content property :


We will set the necessary properties by binding.

MVVM Data Model


In a broad sense, any application solves a specific problem. The model should fully reflect the data in the problem solved by the application. We will simplify the example and exclude the normals - they will be determined by default. Normals are important for displaying textures or fills when calculating illumination.

We select the main interfaces that define the entities of the MVVM data model and their relationships:

interface IModel3DSet {
        string Description { get; set; }
        ICollection Models { get; }
    }
interface IModel3D {
        string Description { get; set; }
        ICollection Points { get; }
        ICollection 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);
    }

The specific implementation is straightforward. To warn about changes in properties, we use the familiar INotifyPropertyChanged .

ViewModel


As a base class for ViewModel we use:

public abstract class BaseVm : Notifier {
        TModel _model;
        public TModel Model {
            get { return _model; }
            set {
                _model = value;
                NotifyWithCallerPropName();
            }
        }
    }
public abstract class BaseVm : BaseVm {
        public BaseVm(TModel model = default(TModel), TParentVM parentVM = default(TParentVM)) {
            Model = model;
            Parent = parentVM;
        }
        public TParentVM Parent { get; }
    }

Such a structure is convenient in that it allows you to move through the ViewModel hierarchy in bindings. The Vector3D , Triangle3D , Point3D classes are simple, therefore it is not necessary to create ViewModel for them. So we need only two ViewModel classes - Model3DSetVm and Model3DVm .

Representation


To build views, we use WPF substitution using the DataType = "{x: Type local: Type}" attribute when declaring a DataTemplate in resource dictionaries. Otherwise, the implementation is standard. The demo application looks like this:



What else do you need to know


  1. In WPF, the TriangleIndices property is optional. If it is not specified, by default triangles will be created for each triple of points. For this reason, even with an empty set of triangles, faces are displayed.

  2. To create a binding for a collection of models, you can use the binding from our example, but substitute an instance of Model3DGroup instead of GeometryModel3D . You cannot use binding to the Children Viewport3D property for this .

  3. If we change the geometry or the axis of rotation, the model is rebuilt, the image will twitch - the animation will start again. To ensure that the process does not interrupt, save the intermediate values ​​of the model and apply animation to them.

A project with an example can be found here ...

I would be glad if the article helps you in something ...

Also popular now: