Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 4096454
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 20, 20262026-05-20T20:02:54+00:00 2026-05-20T20:02:54+00:00

I have a WPF 3D scene where I can pan, rotate and zoom using

  • 0

I have a WPF 3D scene where I can pan, rotate and zoom using the TrackballDecorator from the 3DTools library. I would like to save the camera settings (transformation) and be able to re-apply them when the application restarts the next time (so the view is restored).

I tried to save each individual value of the Camera:

private void SaveCameraSettings()
{
  var d = Properties.Settings.Default;
  d.CameraPositionX = camera.Position.X;
  d.CameraPositionY = camera.Position.Y;
  ...
  d.Save();
}

This doesn’t work, I guess because those settings are not updated according to the transformations applied to the camera (I always get the initial values set in xaml).

I checked the the Transformation3D class but couldn’t find any way to set its value…

The problem is what values do I need to get from the PerspectiveCamera in order to be able to restore it the way it was when I closed my application the last time. The camera is set to a default position (in Xaml), then a transformation is applied to this camera by the TrackBallDecorator. How can I save this transformation (what values to store)? And how can I re-apply them at a later time?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-20T20:02:55+00:00Added an answer on May 20, 2026 at 8:02 pm

    This is going to be a bit long, so bear with me…

    1st, you need to modify the 3DTools library so you can apply a transformation to the TrackballDecorator as follow:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;
    using System.Windows.Input;
    
    namespace _3DTools
    {
      public class TrackballDecorator : Viewport3DDecorator
      {
    
        #region Private Members
    
        private Point m_PreviousPosition2D;
        private Vector3D m_PreviousPosition3D = new Vector3D(0, 0, 1);
    
        private Transform3DGroup m_Transform;
        private ScaleTransform3D m_Scale = new ScaleTransform3D();
        private AxisAngleRotation3D m_Rotation = new AxisAngleRotation3D();
        private TranslateTransform3D m_Translate = new TranslateTransform3D();
    
        private readonly Border m_EventSource;
    
        #endregion
    
        #region Constructor
    
        public TrackballDecorator()
        {
          TranslateScale = 10;
          ZoomScale = 1;
          RotateScale = 1;
          // the transform that will be applied to the viewport 3d's camera
          m_Transform = new Transform3DGroup();
          m_Transform.Children.Add(m_Scale);
          m_Transform.Children.Add(new RotateTransform3D(m_Rotation));
          m_Transform.Children.Add(m_Translate);
    
          // used so that we always get events while activity occurs within
          // the viewport3D
          m_EventSource = new Border { Background = Brushes.Transparent };
    
          PreViewportChildren.Add(m_EventSource);
        }
    
        #endregion
    
        #region Properties
    
        /// <summary>
        /// A transform to move the camera or scene to the trackball's
        /// current orientation and scale.
        /// </summary>
        public Transform3DGroup Transform
        {
          get { return m_Transform; }
          set
          {
            m_Transform = value;
            m_Scale = m_Transform.GetScaleTransform3D();
            m_Translate = m_Transform.GetTranslateTransform3D();
            m_Rotation = m_Transform.GetRotateTransform3D().Rotation as AxisAngleRotation3D;
            ApplyTransform();
          }
        }
    
        public double TranslateScale { get; set; }
    
        public double RotateScale { get; set; }
    
        public double ZoomScale { get; set; }
    
        #endregion
    
        #region Event Handling
    
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
          base.OnMouseDown(e);
    
          m_PreviousPosition2D = e.GetPosition(this);
          m_PreviousPosition3D = ProjectToTrackball(ActualWidth,
                                                   ActualHeight,
                                                   m_PreviousPosition2D);
          if (Mouse.Captured == null)
          {
            Mouse.Capture(this, CaptureMode.Element);
          }
        }
    
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
          base.OnMouseUp(e);
    
          if (IsMouseCaptured)
          {
            Mouse.Capture(this, CaptureMode.None);
          }
        }
    
        protected override void OnMouseMove(MouseEventArgs e)
        {
          base.OnMouseMove(e);
    
          if (IsMouseCaptured)
          {
            Point currentPosition = e.GetPosition(this);
    
            // avoid any zero axis conditions
            if (currentPosition == m_PreviousPosition2D) return;
    
            // Prefer tracking to zooming if both buttons are pressed.
            if (e.LeftButton == MouseButtonState.Pressed)
            {
              Track(currentPosition);
            }
            else if (e.RightButton == MouseButtonState.Pressed)
            {
              Zoom(currentPosition);
            }
            else if (e.MiddleButton == MouseButtonState.Pressed)
            {
              Translate(currentPosition);
            }
    
            m_PreviousPosition2D = currentPosition;
    
            ApplyTransform();
          }
        }
    
        private void ApplyTransform()
        {
          Viewport3D viewport3D = Viewport3D;
          if (viewport3D != null)
          {
            if (viewport3D.Camera != null)
            {
              if (viewport3D.Camera.IsFrozen)
              {
                viewport3D.Camera = viewport3D.Camera.Clone();
              }
    
              if (viewport3D.Camera.Transform != m_Transform)
              {
                viewport3D.Camera.Transform = m_Transform;
              }
            }
          }
        }
    
        #endregion Event Handling
    
        private void Track(Point currentPosition)
        {
          var currentPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, currentPosition);
    
          var axis = Vector3D.CrossProduct(m_PreviousPosition3D, currentPosition3D);
          var angle = Vector3D.AngleBetween(m_PreviousPosition3D, currentPosition3D);
    
          // quaterion will throw if this happens - sometimes we can get 3D positions that
          // are very similar, so we avoid the throw by doing this check and just ignoring
          // the event 
          if (axis.Length == 0) return;
    
          var delta = new Quaternion(axis, -angle);
    
          // Get the current orientantion from the RotateTransform3D
          var r = m_Rotation;
          var q = new Quaternion(m_Rotation.Axis, m_Rotation.Angle);
    
          // Compose the delta with the previous orientation
          q *= delta;
    
          // Write the new orientation back to the Rotation3D
          m_Rotation.Axis = q.Axis;
          m_Rotation.Angle = q.Angle;
    
          m_PreviousPosition3D = currentPosition3D;
        }
    
        private static Vector3D ProjectToTrackball(double width, double height, Point point)
        {
          var x = point.X / (width / 2);    // Scale so bounds map to [0,0] - [2,2]
          var y = point.Y / (height / 2);
    
          x = x - 1;                           // Translate 0,0 to the center
          y = 1 - y;                           // Flip so +Y is up instead of down
    
          var z2 = 1 - x * x - y * y;       // z^2 = 1 - x^2 - y^2
          var z = z2 > 0 ? Math.Sqrt(z2) : 0;
    
          return new Vector3D(x, y, z);
        }
    
        private void Zoom(Point currentPosition)
        {
          var yDelta = currentPosition.Y - m_PreviousPosition2D.Y;
    
          var scale = Math.Exp(yDelta / 100) / ZoomScale;    // e^(yDelta/100) is fairly arbitrary.
    
          m_Scale.ScaleX *= scale;
          m_Scale.ScaleY *= scale;
          m_Scale.ScaleZ *= scale;
        }
    
        private void Translate(Point currentPosition)
        {
          // Calculate the panning vector from screen(the vector component of the Quaternion
          // the division of the X and Y components scales the vector to the mouse movement
          var qV = new Quaternion(((m_PreviousPosition2D.X - currentPosition.X) / TranslateScale),
          ((currentPosition.Y - m_PreviousPosition2D.Y) / TranslateScale), 0, 0);
    
          // Get the current orientantion from the RotateTransform3D
          var q = new Quaternion(m_Rotation.Axis, m_Rotation.Angle);
          var qC = q;
          qC.Conjugate();
    
          // Here we rotate our panning vector about the the rotaion axis of any current rotation transform
          // and then sum the new translation with any exisiting translation
          qV = q * qV * qC;
          m_Translate.OffsetX += qV.X;
          m_Translate.OffsetY += qV.Y;
          m_Translate.OffsetZ += qV.Z;
        }
    
      }
    
    }
    

    The GetXXXTransform3D methods are extension methods defined as follow:

    public static ScaleTransform3D GetScaleTransform3D(this Transform3DGroup transform3DGroup)
    {
      ScaleTransform3D scaleTransform3D = null;
      if (transform3DGroup != null)
      {
        foreach (var transform in transform3DGroup.Children)
        {
          scaleTransform3D = transform as ScaleTransform3D;
          if (scaleTransform3D != null) return scaleTransform3D;
        }
      }
      return scaleTransform3D;
    }
    
    public static RotateTransform3D GetRotateTransform3D(this Transform3DGroup transform3DGroup)
    {
      RotateTransform3D rotateTransform3D = null;
      if (transform3DGroup != null)
      {
        foreach (var transform in transform3DGroup.Children)
        {
          rotateTransform3D = transform as RotateTransform3D;
          if (rotateTransform3D != null) return rotateTransform3D;
        }
      }
      return rotateTransform3D;
    }
    
    public static TranslateTransform3D GetTranslateTransform3D(this Transform3DGroup transform3DGroup)
    {
      TranslateTransform3D translateTransform3D = null;
      if (transform3DGroup != null)
      {
        foreach (var transform in transform3DGroup.Children)
        {
          translateTransform3D = transform as TranslateTransform3D;
          if (translateTransform3D != null) return translateTransform3D;
        }
      }
      return translateTransform3D;
    }
    

    2nd, you need to declare a Transform to your PerspectiveCamera as follow:
    (the example is taken from Sasha Barber’s Elements3D project which I used to test this)

    <Tools:TrackballDecorator x:Name="tbViewPort">
    
      <Viewport3D x:Name="vpFeeds">
    
        <Viewport3D.Camera>
          <PerspectiveCamera x:Name="camera" Position="-2,2,40" LookDirection="2,-2,-40" FieldOfView="90">
            <PerspectiveCamera.Transform>
              <Transform3DGroup />
            </PerspectiveCamera.Transform>
          </PerspectiveCamera>
        </Viewport3D.Camera>
    
        <ContainerUIElement3D x:Name="container" />
    
        <ModelVisual3D x:Name="model">
          <ModelVisual3D.Content>
            <DirectionalLight Color="White" Direction="-1,-1,-1" />
          </ModelVisual3D.Content>
        </ModelVisual3D>
    
      </Viewport3D>
    </Tools:TrackballDecorator>
    

    3rd, since we are going to store each part of the whole transformation in a separate value, you need to create the relevant properties in your settings file, i.e. CameraScaleX, CameraScaleY, CameraScaleZ, CameraTranslateX, CameraTranslateY, CameraTranslateZ, CameraRotateAxisX, CameraRotateAxisY, CameraRotateAxisZ and CameraRotateAngle. All are of type double and are stored in User scope.

    4th and last step is to actually save and load these settings into the camera using the following code:

    private void SaveCameraSettings()
    {
      var transform3DGroup = camera.Transform as Transform3DGroup;
      if (transform3DGroup != null)
      {
        foreach (var transform in transform3DGroup.Children)
        {
          var scale = transform as ScaleTransform3D;
          if (scale != null) SaveCameraSetting(scale);
          var rotate = transform as RotateTransform3D;
          if (rotate != null) SaveCameraSetting(rotate);
          var translate = transform as TranslateTransform3D;
          if (translate != null) SaveCameraSetting(translate);
        }
        Settings.Default.Save();
      }
    }
    
    private static void SaveCameraSetting(ScaleTransform3D transform)
    {
      Properties.Settings.Default.CameraScaleX = transform.ScaleX;
      Properties.Settings.Default.CameraScaleY = transform.ScaleY;
      Properties.Settings.Default.CameraScaleZ = transform.ScaleZ;
    }
    
    private static void SaveCameraSetting(RotateTransform3D transform)
    {
      var axisAngleRotation3D = transform.Rotation as AxisAngleRotation3D;
      if (axisAngleRotation3D != null)
      {
        Properties.Settings.Default.CameraRotateAxisX = axisAngleRotation3D.Axis.X;
        Properties.Settings.Default.CameraRotateAxisY = axisAngleRotation3D.Axis.Y;
        Properties.Settings.Default.CameraRotateAxisZ = axisAngleRotation3D.Axis.Z;
        Properties.Settings.Default.CameraRotateAngle = axisAngleRotation3D.Angle;
      }
    }
    
    private static void SaveCameraSetting(TranslateTransform3D transform)
    {
      Properties.Settings.Default.CameraTranslateX = transform.OffsetX;
      Properties.Settings.Default.CameraTranslateY = transform.OffsetY;
      Properties.Settings.Default.CameraTranslateZ = transform.OffsetZ;
    }
    
    private void LoadCameraPosition()
    {
      var d = Settings.Default;
    
      var transform3DGroup = new Transform3DGroup();
    
      var scaleTransform3D = new ScaleTransform3D(d.CameraScaleX, d.CameraScaleY, d.CameraScaleZ);
      var translateTransform3D = new TranslateTransform3D(d.CameraTranslateX, d.CameraTranslateY, d.CameraTranslateZ);
      var axisAngleRotation3D = new AxisAngleRotation3D(new Vector3D(d.CameraRotateAxisX, d.CameraRotateAxisY, d.CameraRotateAxisZ),
                                                        d.CameraRotateAngle);
      var rotateTransform3D = new RotateTransform3D(axisAngleRotation3D);
    
      transform3DGroup.Children.Add(scaleTransform3D);
      transform3DGroup.Children.Add(translateTransform3D);
      transform3DGroup.Children.Add(rotateTransform3D);
    
      tbViewPort.Transform = transform3DGroup;
    }
    

    Hopefully, I didn’t forget anything. If you need more help or don’t understand something, please don’t hesitate to ask 😉

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have WPF application that has a login form. I would like to make
I have a scene in my WPF project with about 2000 different user controls:
I have a WPF window for editing database information, which is represented using an
I have WPF application and a window in it. Lets have something like this
Suppose you have WPF Window composed of many elements that are using DataTemplates /
I have WPF control hosted inside a WinForms control using ElementHost. The WinForms control
I have WPF DataGrid (VS2010 C#). I copied the data from DataGrid to Clipboard
In my WPF Window_Loaded event handler I have something like this: System.Threading.Tasks.Task.Factory.StartNew(() => {
Suppose I have a simple WPF 3D scene set up with a single rectangle
I have a WPF project using CM. I have a progress bar that I

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.