Perspective : relative WPF 3D transforms

Perspective : relative WPF 3D transforms

By default, the position, the scale factor and the rotation angle of a WPF 3D model have to be defined by a transform (or a Transform3DGroup object which gathers several transforms).

Indeed, transforms are supported by the graphics processor (GPU). Without them, the positioning and sizing calculations would use the CPU, which could cause performance problems (displacement of large models in particular).

That's why classes inherited from Visual3D (including those of Perspective) do not expose property indicating their position.

But it may surprise at first. And this may be inconvenient for some scenarios, eg when one wishes to apply successively (and relatively) several transforms. Indeed, only one transform (or one group of transforms) is applicable at a given time, and relatively to the initial coordinates.

To address this problem, I developed the RelativeTransformer class in the Perspective.Wpf3D namespace of the Perspective 2 beta Library, which can be downloaded here .

This class has methods that allow to apply successive displacements, rotations and scalings to a Visual3D object. This class remembers for the current position and the transform matrix of the Visual3D. Some attached Properties (Position, Matrix) provide access to these informations. The resulting transform is available through the MatrixTransform attached property.

Take the example of a basic Perspective model : a Box3D defined in XAML as follows :

...
<Window.Resources>
    <SpecularMaterial x:Key="Specular" SpecularPower="100.0" Brush="White"/>
    <MaterialGroup x:Key="GlossyMaterial">
        <DiffuseMaterial Brush="Goldenrod"/>
        <StaticResource ResourceKey="Specular"/>
    </MaterialGroup>
...
</Window.Resources>
...
<p:Workshop3D>
    <p:XyzAxis3D Length="4.0"/>
    <p:Box3D x:Name="box" Material="{StaticResource GlossyMaterial}" />
</p:Workshop3D>
...

Initial Box3D model

Moving

The MoveTo method of the RelativeTransformer class moves the Visual3D to a specific position, here a Box3D to the coordinates (0,1,0) :

public partial class Window1 : Window
{
    RelativeTransformer _transformer = new RelativeTransformer();
...
    private void t1_Click(object sender, RoutedEventArgs e)
    {
        _transformer.MoveTo(box, 0, 1, 0);
    }
...
}

Box3D moved by RelativeTransformer.MoveTo

The MoveTo method can be called multiple times. Each time, the resultant position is stored in the Position property.

The Translate method moves the Visual3D specifying a vector or a shift of coordinates on the current position, as here (2 on the X axis, -1 on the Y axis):

_transformer.Translate(box, 2, -1, 0);

Box3D moved by RelativeTransformer.Translate

The MoveTo and Translate methods modify the current position, available in the Position attached property.

Rotations

The RotateX method of the RelativeTransformer class applies a rotation around the x-axis.

_transformer.RotateX(box, 45);

Box3D - First RelativeTransformer.RotateX call

A second call applies the rotation in relative to the previous one :

Box3D - Second RelativeTransformer.RotateX call

Similarly, the Rotate RotateY and RotateZ methods apply a rotation around the y- and z-axis. And the RotateXYZ and RotateZYX methods apply a rotation on the 3 axis, in the order indicated by the name of the method.

Scaling

The Scale method of the RelativeTransformer class applies a scale factor to a Visual3D :

_transformer.Scale(box, 0.5);

Box3D - First RelativeTransformer.Scale call

A second call applies a scaling in relative to the previous one :

Box3D - Second RelativeTransformer.Scale call

Compatibility with initial transforms

If transforms have been previously applied, they are retained. The internal matrix transform managed by RelativeTransformer is automatically combined with the initial transforms.

<Window.Resources>
...    
    <ScaleTransform3D 
        x:Key="ModelTransform" 
        ScaleX="1.0" ScaleY="2.0" ScaleZ="1.0" />
</Window.Resources>
...
<p:Workshop3D>
...
    <p:Box3D 
        x:Name="box" 
        Material="{StaticResource GlossyMaterial}" 
        Transform="{StaticResource ModelTransform}"/>
</p:Workshop3D>
_transformer.MoveTo(box, 0, 1, 0);
_transformer.Translate(box, 2, -1, 0);
_transformer.RotateX(box, 45);
_transformer.Scale(box, 0.5);

Box3D processed by RelativeTransformer with initial transform

About this article

Author : Olivier Dewit

History :