Page 1 of 1

Animation and 3D I think this should be its own forum...

#1 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 08 March 2010 - 06:34 PM

Lately, I've had some free time on my hands and decided to play with the 3D and Animation classes in .Net. I think these deserve a 'Tutorial' section all by themselves. They are really interesting and allow you to do amazing things quickly and easily.

At the bottom of this post you'll see a digital image of a sailboat, something fun to play with.

The first thing I wanted to do is to simply rotate this image using animation.
So I create a WPF Application in Visual Studio. I always change the 'Window1' default name to something more meaningfull so I've named the project Animation3DProject and changed the name of Window1 to Mainwindow.

(Right click on 'Window1.xaml' and change the name to 'Mainwindow.xaml'. Open 'App.xaml' and change the 'StartupUri="Window1.xaml"' to 'StartupUri="Mainwindow.xaml"'. Open 'Mainwindow.xaml' and change all occurences of 'Window1' to 'MainWindow'. Open 'Mainwindow.xaml.cs and change all occurences of 'Window1' to 'MainWindow'.)

My main window definition is pretty simple. I want to show the image, then automate its rotation first. Here is the xaml code:
<Window x:Class="WpfImageRotateProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Loaded="Window_Loaded">
    <Image Name="_img" 
           Margin="2,2,2,2" 
           Source="Images\1969458_11.jpg"
           MouseEnter="_img_MouseEnter"
           MouseLeave="_img_MouseLeave"/>
</Window>



Notice the <Image> element. I've created a folder in the project called 'Images', copied the file '1969458_11.jpg' to the Images folder and Added the Image by right clicking on the Images folder in Solution Explorer and added the jpeg file as an existing item so I could reference it with the line: 'Source="Images\1969458_11.jpg"'.

Just looking at the xaml code you'll see two event handlers, MouseEnter and MouseLeave. I want the image to start rotating and rotate once when the mouse 'enters' the content area of 'MainWindow'. Here is the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace WpfImageRotateProject
{
    /// <summary>
    /// Interaction logic for Mainwindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public DoubleAnimation _da;
        public RotateTransform _rt;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(3)));
            _rt = new RotateTransform();
            _img.RenderTransform = _rt;
            _img.RenderTransformOrigin = new Point(0.5, 0.5);
        }

        private void _img_MouseEnter(object sender, MouseEventArgs e)
        {
            _da.Duration = new Duration(TimeSpan.FromSeconds(3));
            _rt.BeginAnimation(RotateTransform.AngleProperty, _da);
        }

        private void _img_MouseLeave(object sender, MouseEventArgs e)
        {
        }
    }
}



When I ran this code I noticed that as the image rotates it jumps because the mouse cursor 'leaves' the image if you position it over a corner. This causes the image to
BeginAnimation again as the MouseEnter event gets handled. I found a 'Completed' event for the 'DoubleAnimation' object and added a handler so that my Mainwindow.xaml.cs file looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace WpfImageRotateProject
{
    /// <summary>
    /// Interaction logic for Mainwindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public DoubleAnimation _da;
        public RotateTransform _rt;
        public bool _bAnimationComplete;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _bAnimationComplete = true;
            _da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(3)));
            _da.Completed += new EventHandler(_da_Completed);
            _rt = new RotateTransform();
            _img.RenderTransform = _rt;
            _img.RenderTransformOrigin = new Point(0.5, 0.5);
        }

        void _da_Completed(object sender, EventArgs e)
        {
            _bAnimationComplete = true;
        }

        private void _img_MouseEnter(object sender, MouseEventArgs e)
        {
            if (_bAnimationComplete)
            {
                _bAnimationComplete = false;
                _da.Duration = new Duration(TimeSpan.FromSeconds(3));
                _rt.BeginAnimation(RotateTransform.AngleProperty, _da);
            }
        }
    }
}



And now my image rotates once and only once until it 'completes' and then it is eligible to rotate again. I noticed that it rotates around the middle of the frame. It would be nice if I could get it to rotate around the bottom of the mast. I found CenterX and CenterY that specify the center of rotation. So I set CenterX = _img.ActualWidth/2; and CenterY = _img.ActualHeight/2; to see what would happen.

And as you would expect, you can see the image rotate around the lower right corner. So, indeed, CenterX=CenterY=0; means that the image rotates around its center. Setting CenterX = -_img.ActualWidth/2; and CenterY = -_img.ActualHeight/2; causes the image to rotate around its upper left corner. So I measured the position of the center of the mast as a percent of the total width and height. If I want the image to rotate around the center of the mast I simply set the point of rotation as CenterX = -(.5 - 0.421875)*_img.ActualWidth; CenterY = -(.5 - .36715)*_img.ActualHeight; the image should rotate around the center of the mast where it attaches to the deck and it does.

I thought it would be nice to play with RotateTransform on other objects too, like TextBlock. If you create a new WPF application and replace the <Grid></Grid> with the following xaml code, you can animate the color, the FontSize and rotate the pieces. Here is the xaml code.
    <FlowDocumentReader ViewingMode="Scroll" Name="_flowDocReader" >
        <FlowDocumentReader.RenderTransform>
            <RotateTransform Angle="0" CenterX="150" CenterY="150"/>
        </FlowDocumentReader.RenderTransform>
        <FlowDocumentReader.Background>
            <SolidColorBrush Color="AliceBlue"/>
        </FlowDocumentReader.Background>
        <FlowDocumentReader.Triggers>
            <EventTrigger RoutedEvent="FlowDocumentReader.Loaded">
                <BeginStoryboard>
                    <Storyboard TargetProperty="FontSize">
                        <DoubleAnimation From="70" To="11" Duration="0:0:3" AccelerationRatio="0" DecelerationRatio=".2"/>
                    </Storyboard>
                </BeginStoryboard>
                <BeginStoryboard>
                    <Storyboard TargetProperty="RenderTransform.(RotateTransform.Angle)">
                        <DoubleAnimation From="0" To="360" Duration="0:0:3" AccelerationRatio=".2" DecelerationRatio=".2"/>
                    </Storyboard>
                </BeginStoryboard>
                <BeginStoryboard>
                    <Storyboard TargetProperty="Background.(SolidColorBrush.Color)">
                        <ColorAnimation From="DarkOrange" To="LightBlue" Duration="0:0:3" AccelerationRatio=".1" DecelerationRatio=".5"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger RoutedEvent="FlowDocumentReader.MouseEnter">
                <BeginStoryboard>
                    <Storyboard TargetProperty="Opacity">
                        <DoubleAnimation From="1" To="0" Duration="0:0:3" />
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger RoutedEvent="FlowDocumentReader.MouseLeave">
                <BeginStoryboard>
                    <Storyboard TargetProperty="Opacity">
                        <DoubleAnimation From="0" To="1" Duration="0:0:3"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </FlowDocumentReader.Triggers>
        <FlowDocument Name="_flowDoc" Foreground="Black" >
            <Paragraph Name="Heading" TextAlignment="Center">
                <TextBlock>
                    <TextBlock.RenderTransform>
                        <RotateTransform CenterX="150"/>
                    </TextBlock.RenderTransform>
                    <TextBlock.Background>
                        <SolidColorBrush/>
                    </TextBlock.Background>
                    <TextBlock.Triggers>
                        <EventTrigger RoutedEvent="TextBlock.Loaded">
                            <BeginStoryboard>
                                <Storyboard TargetProperty="FontSize">
                                    <DoubleAnimation From="30" To="14" Duration="0:0:3" AccelerationRatio="0" DecelerationRatio=".2"/>
                                </Storyboard>
                            </BeginStoryboard>
                            <BeginStoryboard>
                                <Storyboard TargetProperty="RenderTransform.(RotateTransform.Angle)">
                                    <DoubleAnimation From="3600" To="0" Duration="0:0:3" AccelerationRatio=".2" DecelerationRatio=".2"/>
                                </Storyboard>
                            </BeginStoryboard>
                            <BeginStoryboard>
                                <Storyboard TargetProperty="Background.(SolidColorBrush.Color)">
                                    <ColorAnimation From="DarkOrange" To="LightBlue" Duration="0:0:3" AccelerationRatio=".1" DecelerationRatio=".5"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </TextBlock.Triggers>
                    Declaration of Independence
                </TextBlock>
            </Paragraph>
            <Paragraph TextAlignment="Left" >
                <TextBlock TextWrapping="Wrap">
                    <TextBlock.RenderTransform>
                        <RotateTransform CenterX="150"/>
                    </TextBlock.RenderTransform>
                    <TextBlock.Background>
                        <SolidColorBrush/>
                    </TextBlock.Background>
                    <TextBlock.Triggers>
                        <EventTrigger RoutedEvent="TextBlock.Loaded">
                            <BeginStoryboard>
                                <Storyboard TargetProperty="FontSize">
                                    <DoubleAnimation From="30" To="14" Duration="0:0:3" AccelerationRatio="0" DecelerationRatio=".2"/>
                                </Storyboard>
                            </BeginStoryboard>
                            <BeginStoryboard>
                                <Storyboard TargetProperty="RenderTransform.(RotateTransform.Angle)">
                                    <DoubleAnimation From="0" To="720" Duration="0:0:3" AccelerationRatio=".2" DecelerationRatio=".2"/>
                                </Storyboard>
                            </BeginStoryboard>
                            <BeginStoryboard>
                                <Storyboard TargetProperty="Background.(SolidColorBrush.Color)">
                                    <ColorAnimation From="DarkOrange" To="LightBlue" Duration="0:0:3" AccelerationRatio=".1" DecelerationRatio=".5"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </TextBlock.Triggers>
                        When, in the course of human events, it becomes necessary for one people to dissolve the political bonds which have connected 
                        them with another, and to assume among the powers of the earth, the separate and equal station to which the laws of nature and 
                        of nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which 
                        impel them to the separation. 
                </TextBlock>
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain 
                unalienable rights, that among these are life, liberty and the pursuit of happiness. That to secure these rights, governments 
                are instituted among men, deriving their just powers from the consent of the governed. That whenever any form of government 
                becomes destructive to these ends, it is the right of the people to alter or to abolish it, and to institute new government, 
                laying its foundation on such principles and organizing its powers in such form, as to them shall seem most likely to effect 
                their safety and happiness. Prudence, indeed, will dictate that governments long established should not be changed for light 
                and transient causes; and accordingly all experience hath shown that mankind are more disposed to suffer, while evils are 
                sufferable, than to right themselves by abolishing the forms to which they are accustomed. But when a long train of abuses and 
                usurpations, pursuing invariably the same object evinces a design to reduce them under absolute despotism, it is their right, 
                it is their duty, to throw off such government, and to provide new guards for their future security. --Such has been the patient 
                sufferance of these colonies; and such is now the necessity which constrains them to alter their former systems of government. 
                The history of the present King of Great Britain is a history of repeated injuries and usurpations, all having in direct object 
                the establishment of an absolute tyranny over these states. To prove this, let facts be submitted to a candid world.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has refused his assent to laws, the most wholesome and necessary for the public good.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has forbidden his governors to pass laws of immediate and pressing importance, unless suspended in their operation till his 
                assent should be obtained; and when so suspended, he has utterly neglected to attend to them.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has refused to pass other laws for the accommodation of large districts of people, unless those people would relinquish the 
                right of representation in the legislature, a right inestimable to them and formidable to tyrants only.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has called together legislative bodies at places unusual, uncomfortable, and distant from the depository of their public records, 
                for the sole purpose of fatiguing them into compliance with his measures.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has dissolved representative houses repeatedly, for opposing with manly firmness his invasions on the rights of the people.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has refused for a long time, after such dissolutions, to cause others to be elected; whereby the legislative powers, incapable 
                of annihilation, have returned to the people at large for their exercise; the state remaining in the meantime exposed to all the 
                dangers of invasion from without, and convulsions within.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has endeavored to prevent the population of these states; for that purpose obstructing the laws for naturalization of foreigners; 
                refusing to pass others to encourage their migration hither, and raising the conditions of new appropriations of lands.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has obstructed the administration of justice, by refusing his assent to laws for establishing judiciary powers.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has made judges dependent on his will alone, for the tenure of their offices, and the amount and payment of their salaries.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has erected a multitude of new offices, and sent hither swarms of officers to harass our people, and eat out their substance.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has kept among us, in times of peace, standing armies without the consent of our legislature.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has affected to render the military independent of and superior to civil power.
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                He has combined with others to subject us to a jurisdiction foreign to our constitution, and unacknowledged by our laws; giving his assent to their acts of pretended legislation:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For quartering large bodies of armed troops among us:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For protecting them, by mock trial, from punishment for any murders which they should commit on the inhabitants of these states:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For cutting off our trade with all parts of the world:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For imposing taxes on us without our consent:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For depriving us in many cases, of the benefits of trial by jury:
            </Paragraph>
            <Paragraph TextAlignment="Left" FontSize="10pt">
                For transporting us beyond seas to be tried for pretended offenses:
            </Paragraph >
        </FlowDocument>
    </FlowDocumentReader>


What I'd really like to do is to create a cube and display an image on each face of the cube with a 3D viewer so I can rotate the cube and then rotate the image on the faces of the cube.

Where to start? First I have need a 3D cube. In 3D all objects are described by a set of triangles that describe the surface of the object. This 3D surface of triangles is called a mesh. Each triangle has a front and a back side and only the 'front' side is rendered by the rendering engine. WPF uses a 'right handed coordinate system determine the front and back of the object. The front of a surface triangle is facing you if you enumerate the vertices in a counter clockwise order.

The 'MeshGeometry3D' is the class that lets you describe or build a 3D shape. It has a couple of properties that are important. The 'Positions' property holds the x-y-z definitions of all the vertices of all the triangles that define your surface. The 'TriangleIndices' property sets the order in which each triangle's vertex positions are travelled to form the triangle and determines if the triangle is a front or a back. According to the documentation setting the 'TriangleIndices' property is optional. If you don't set the 'TriangleIndices' property, every three 'Positions' for a triangle front face. The counter clockwise order is assumed.

You can set the 'Positions' and 'TriangleIndices' properties in xaml or in the code. For my money it is more readable in the code but I'll do it xaml first.

First I need to remove the image from the Mainwindow.Content and replace it with a 'Viewport3D' object. Here is my modified Mainwindow.xaml file:
<Window x:Class="WpfImageRotateProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Loaded="Window_Loaded">
    <Viewport3D Name="_vp3D">
        
    </Viewport3D>
</Window>



And the associated Mainwindow.xaml.cs file with everything fixed so it compiles again:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;

namespace WpfImageRotateProject
{
    /// <summary>
    /// Interaction logic for Mainwindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public DoubleAnimation _da;
        public RotateTransform _rt;
        public bool _bAnimationComplete;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
        }

        void _da_Completed(object sender, EventArgs e)
        {
        }

        private void _img_MouseEnter(object sender, MouseEventArgs e)
        {
        }
    }
}



The components to create a simple 3D scene are:
A camera to project the image of the 3D object on to a flat plane
A mesh to specify the 3D object
A material to reflect the light
A light source to illuminate the object.

First a camera, in this case a 'PerspectiveCamera'. We need to understand the various properties of the camera and what they do. So lets start with a simple cube. The first piece of information that we need is what kind of coordinate system are we using? WPF uses a 'righthand coordinate system' but how that coordinate system is oriented relative to you screen is a function of the camera's 'Position' and 'LookDirection'. Initially, consider the y-axis pointing positive up to the top of your screen, the x-axis pointing to the right of your screen and the z-axis pointing perpendicularly out towards you. This is a 'right hand coordinate system'.

Here is the xaml to define a cube.
<Window x:Class="WpfImageRotateProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Loaded="Window_Loaded">
    <Viewport3D Name="_vp3D">
        <Viewport3D.Camera>
            <PerspectiveCamera Position="80,80,80" LookDirection="-80,-80,-80 " UpDirection="0,0,1" />
        </Viewport3D.Camera>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup>
                    <DirectionalLight Color="White" Direction="-1, -1, -3"/>
                    <GeometryModel3D>
                        <GeometryModel3D.Geometry>
                            <!-- the first 4 points are the points of the top cube face,
                                 lower right, upper right, upper left lower left.
                                 the second 4 points are the bottom cube face
                                 Then we need to define the triangles going counter clockwise
                                 that make up the cube faces
                            -->
                            <MeshGeometry3D Positions="0,0,0 40,0,0 40,40,0 0,40,0 
                                                       0,0,40 40,0,40 40,40,40 0,40,40"
                                            TriangleIndices="0 1 3 1 2 3 
                                                             0 4 3 4 7 3
                                                             4 6 7 4 5 6
                                                             0 4 1 1 4 5
                                                             1 2 6 6 5 1
                                                             2 3 7 7 6 2"/>
                        </GeometryModel3D.Geometry>
                        <GeometryModel3D.Material>
                            <DiffuseMaterial Brush="Red"/>
                        </GeometryModel3D.Material>
                    </GeometryModel3D>
                </Model3DGroup>
            </ModelVisual3D.Content>
        </ModelVisual3D>
    </Viewport3D>
</Window>



There are several things you can do that help you visualize what is happening. Replace the 'Positions="0,0,0 40,0,0 40,40,0 0,40,0 0,0,40 40,0,40 40,40,40 0,40,40"' with 'Positions="0,0,0 40,0,0 40,40,0 0,40,0"' and you should see the bottom face of the cube. If you replace 'Positions="0,0,0 40,0,0 40,40,0 0,40,0 0,0,40 40,0,40 40,40,40 0,40,40"' with 'Positions="0,0,40 40,0,40 40,40,40 0,40,40"' you'll see the top face of the cube.

It is also interesting to remove each triangle of the cube one at a time and see the effect. For instance, cut the '7 6 2' from the last line of the TriangleIndices and notice the triangle that is removed. Also notice that the back face of the cube is white but the bottom face is showing red to the inside of the cube.

Remember that the 'reflective' side of the triangle has points that define its indices in a counter clockwise direction. Change the first line of TriangleIndices to '0 3 1 1 3 2' which reverses the order of the points of the triangle. Now the entire interior of the triangle we removed first is white. But if we could turn the cube around and upside down we would now see the red reflective surface.

We can also play with lights, DirectionalLight, AmbientLight, SpotLight and PointLight. Change the '<DirectionalLight Color="White" Direction="-1, -1, -3"/>' line in the xaml to '<PointLight Color="White" Position="30, 30, 30"/>'. PointLights diminish with distance.

Here is a line for a SpotLight, '<SpotLight Color="White" Position="20, 20, 20" InnerConeAngle="20" OuterConeAngle="20" Direction="-20,-20,-20" Range="20"/>'.

If I finish this this tutorial will be significantly longer. I'd be happy to do it if there is interest. The other option is to make several tutorials and split this up into several segments. Any thoughts on direction?

Attached image(s)

  • Attached Image

This post has been edited by StCroixSkipper: 14 March 2010 - 03:29 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Animation and 3D

#2 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 16 March 2010 - 09:29 AM

Now it would be nice to animate the cube, make it move around its axes. Quaternions are still a little bit of a mystery to me, but I found the 'AxisAngleRotation3D' class which lets me specify an angle and an axis around which to move the cube. I've hooked up the animation to a the PreviewMouseLeftButtonDown, PreviewMouseRightButtonDown, and PreviewMouseWheel events so I have a UI action to cause the animation around each action. When I saw all sides of my cube, I had to reorder some of the triangles to make sure that all faces of the cube were 'facing out', so that they were visible as the cube rotated.

Here is the code:
<Window x:Class="WpfImageRotateProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" 
        Width="300"
        Height="300"
        Loaded="Window_Loaded">
    <Viewport3D Name="_vp3D" 
                PreviewMouseLeftButtonDown="_vp3D_PreviewMouseLeftButtonDown" 
                PreviewMouseRightButtonDown="_vp3D_PreviewMouseRightButtonDown"
                PreviewMouseWheel="_vp3D_PreviewMouseWheel">
        <Viewport3D.Camera>
            <PerspectiveCamera  Position="40,10,40" LookDirection="-1,-.2,-1 " UpDirection="0, 1, 0" FieldOfView="30" NearPlaneDistance="40"/>
        </Viewport3D.Camera>
        <ModelVisual3D >
            <ModelVisual3D.Content>
                <Model3DGroup>
                    <DirectionalLight Color="White" Direction="-1,-1,-1" />
                    <GeometryModel3D>
                        <GeometryModel3D.Geometry>
                            <!-- the first 4 points are the points of the top cube face,
                                 lower right, upper right, upper left lower left.
                                 the second 4 points are the bottom cube face
                                 Then we need to define the triangles going counter clockwise
                                 that make up the cube faces
                            -->
                            <MeshGeometry3D Positions="0,0,0 10,0,0 10,10,0 0,10,0 
                                                       0,0,10 10,0,10 10,10,10 0,10,10"
                                            TriangleIndices="0 3 1 1 3 2  
                                                             0 4 3 4 7 3 
                                                             4 6 7 4 5 6 
                                                             0 1 4 1 5 4 
                                                             1 2 6 6 5 1 
                                                             2 3 7 7 6 2"/>
                        </GeometryModel3D.Geometry>
                        <GeometryModel3D.Material>
                            <DiffuseMaterial Brush="Red"/>
                        </GeometryModel3D.Material>
                    </GeometryModel3D>
                </Model3DGroup>
            </ModelVisual3D.Content>
        </ModelVisual3D>
    </Viewport3D>
</Window>



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.IO;

namespace WpfImageRotateProject
{
    /// <summary>
    /// Interaction logic for Mainwindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public DoubleAnimation _da;
        public RotateTransform _rt;
        public bool _bAnimationComplete;

        public double rotateAngle = 0;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            PerspectiveCamera camera = (PerspectiveCamera)_vp3D.Camera;
            Console.WriteLine("Position: X {0}, Y {1}, Z {2}", camera.Position.X, camera.Position.Y, camera.Position.Z);
            Console.WriteLine("LookDir:  X {0}, Y {1}, Z {2}", camera.LookDirection.X, camera.LookDirection.Y, camera.LookDirection.Z);
            Visual3DCollection vp3DChildren = (Visual3DCollection)_vp3D.Children;
            foreach (object child in vp3DChildren)
            {
                if (child.GetType() == typeof(ModelVisual3D))
                {
                    Console.WriteLine(child.ToString());
                    ModelVisual3D modelVisual3D = (ModelVisual3D)child;
                    if(modelVisual3D.Content.GetType() == typeof(Model3DGroup))
                    {
                        Model3DGroup model3DGroup = (Model3DGroup)modelVisual3D.Content;
                        foreach(object obj in model3DGroup.Children)
                        {
                            if (obj.GetType() == typeof(GeometryModel3D))
                            {
                                GeometryModel3D geoModel3D = (GeometryModel3D)obj;
                                if (geoModel3D.Geometry.GetType() == typeof(MeshGeometry3D))
                                {
                                    MeshGeometry3D mesh3D = (MeshGeometry3D)geoModel3D.Geometry;
                                    Console.WriteLine("Number of vectors in Normals list: {0}", mesh3D.Normals.Count);
                                    foreach (Vector3D vector in mesh3D.Normals)
                                    {
                                        Console.WriteLine("{0}, X={1}, Y={2}, Z={3}", vector.ToString(), vector.X, vector.Y, vector.Z);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        void _da_Completed(object sender, EventArgs e)
        {
        }

        private void _img_MouseEnter(object sender, MouseEventArgs e)
        {
        }

        private void _vp3D_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Visual3DCollection vp3DChildren = (Visual3DCollection)_vp3D.Children;
            foreach (object child in vp3DChildren)
            {
                if (child.GetType() == typeof(ModelVisual3D))
                {
                    ModelVisual3D modelVisual = (ModelVisual3D)child;

                    DoubleAnimation da = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(3)));
                    AxisAngleRotation3D aaRotate3d = new AxisAngleRotation3D();
                    aaRotate3d.Angle = 0;
                    aaRotate3d.Axis = new Vector3D(1, 0, 0);
                    RotateTransform3D rt = new RotateTransform3D();
                    rt.Rotation = aaRotate3d;
                    //rt.Transform = aaRotate3d;
                    modelVisual.Transform = rt;
                    _da = new DoubleAnimation();
                    _da.From = 0;
                    _da.To = 360;
                    _da.Duration = new Duration(TimeSpan.FromSeconds(3));
                    aaRotate3d.BeginAnimation(AxisAngleRotation3D.AngleProperty, _da); 
                }
            }
        }

        private void _vp3D_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Visual3DCollection vp3DChildren = (Visual3DCollection)_vp3D.Children;
            foreach (object child in vp3DChildren)
            {
                if (child.GetType() == typeof(ModelVisual3D))
                {
                    ModelVisual3D modelVisual = (ModelVisual3D)child;

                    DoubleAnimation da = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(3)));
                    AxisAngleRotation3D aaRotate3d = new AxisAngleRotation3D();
                    aaRotate3d.Angle = 0;
                    aaRotate3d.Axis = new Vector3D(0, 1, 0);
                    RotateTransform3D rt = new RotateTransform3D();
                    rt.Rotation = aaRotate3d;
                    modelVisual.Transform = rt;
                    _da = new DoubleAnimation();
                    _da.From = 0;
                    _da.To = 360;
                    _da.AutoReverse = true;
                    _da.Duration = new Duration(TimeSpan.FromSeconds(3));
                    aaRotate3d.BeginAnimation(AxisAngleRotation3D.AngleProperty, _da);
                }
            }
        }

        private void _vp3D_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            Visual3DCollection vp3DChildren = (Visual3DCollection)_vp3D.Children;
            foreach (object child in vp3DChildren)
            {
                if (child.GetType() == typeof(ModelVisual3D))
                {
                    ModelVisual3D modelVisual = (ModelVisual3D)child;

                    AxisAngleRotation3D aaRotate3d = new AxisAngleRotation3D();
                    aaRotate3d.Angle = 0;
                    aaRotate3d.Axis = new Vector3D(0, 0, 1);
                    RotateTransform3D rt = new RotateTransform3D();
                    rt.Rotation = aaRotate3d;
                    modelVisual.Transform = rt;

                    DoubleAnimation da = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(3)));
                    _da = new DoubleAnimation();
                    _da.From = 0;
                    _da.To = 360;
                    _da.Duration = new Duration(TimeSpan.FromSeconds(3));
                    aaRotate3d.BeginAnimation(AxisAngleRotation3D.AngleProperty, _da);
                }
            }
        }
    }
}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1