3

我有一个 WPF 3D 场景,我可以使用3DTools libraryTrackballDecorator中的平移、旋转和缩放。我想保存相机设置(转换)并能够在应用程序下次重新启动时重新应用它们(因此视图被恢复)。

我试图保存每个单独的值Camera

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

这不起作用,我猜是因为这些设置没有根据应用于相机的转换进行更新(我总是得到在 xaml 中设置的初始值)。

我检查了 Transformation3D 类,但找不到任何方法来设置它的值......

问题是我需要从 PerspectiveCamera 获取什么值才能恢复到上次关闭应用程序时的状态。相机设置为默认位置(在 Xaml 中),然后由 TrackBallDecorator 对该相机应用变换。我怎样才能保存这个转换(要存储什么值)?我以后如何重新申请?

4

3 回答 3

4

这会有点长,所以请耐心等待...

1st,您需要修改 3DTools 库,以便您可以将转换应用于TrackballDecorator如下:

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;
    }

  }

}

这些GetXXXTransform3D方法是定义如下的扩展方法:

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;
}

2,您需要向Transform您声明 aPerspectiveCamera如下:(
示例取自 Sasha Barber 的Elements3D项目,我曾经对此进行测试)

<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>

第三,由于我们要将整个转换的每个部分存储在一个单独的值中,您需要在设置文件中创建相关属性,即CameraScaleX, CameraScaleY, CameraScaleZ, CameraTranslateX, CameraTranslateY, CameraTranslateZ, CameraRotateAxisX,CameraRotateAxisY和. 所有都是类型并存储在用户范围内。CameraRotateAxisZCameraRotateAngledouble

第 4步也是最后一步是使用以下代码将这些设置实际保存并加载到相机中:

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;
}

希望我没有忘记任何事情。如果您需要更多帮助或不明白的地方,请不要犹豫;-)

于 2011-03-16T13:08:38.663 回答
0

您将需要相机视图矩阵数据和投影矩阵数据。视图矩阵将包含有关相机位置、旋转、缩放和平移的数据,而投影矩阵将包含视野、近平面、远平面和其他数据。

抱歉,由于我没有使用 WPF,因此我无法帮助导出/导入该数据,但是如果它使用与 as3 内置的 Matrix 类有关的任何内容,则可能会暴露原始数据属性,这通常是 as3 向量。对象的矩阵 16 值公开为行排序的浮点值。

于 2010-09-08T23:01:06.503 回答
0

我相信您需要的是 Position、LookDirection、UpDirection、FieldOfView、NearPlaneDistance、FarPlaneDistance。以上所有属性都定义了相机。

于 2011-03-15T09:27:04.837 回答