WPF 3D - Como posso salvar e carregar uma visão Camera?

votos
2

Eu tenho uma cena WPF 3D onde posso mover, girar e zoom utilizando a TrackballDecoratorpartir da biblioteca 3DTools . Eu gostaria de salvar as configurações da câmera (transformação) e ser capaz de voltar a aplicá-las quando o aplicativo for reiniciado na próxima vez (para que a vista é restaurada).

Tentei salvar cada valor individual do Camera:

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

Isso não funciona, eu acho, porque essas configurações não são atualizados de acordo com as transformações aplicadas à câmara (eu sempre obter os valores iniciais estabelecidos em XAML).

Eu chequei a classe a Transformation3D mas não conseguiu encontrar alguma maneira de definir o seu valor ...

O problema é que os valores que eu preciso para começar a partir do PerspectiveCamera, a fim de ser capaz de restaurá-lo do jeito que era quando eu fechei a aplicação da última vez. A câmara está regulada para uma posição padrão (em Xaml), em seguida, uma transformação é aplicada a esta câmara através da TrackBallDecorator. Como posso salvar esta transformação (que valores para armazenar)? E como posso voltar a aplicá-los em um momento posterior?

Publicado 25/11/2009 em 17:41
fonte usuário
Em outras línguas...                            


3 respostas

votos
3

Este vai ser um pouco longo, para ter comigo ...

, é necessário modificar a biblioteca 3DTools para que possa aplicar uma transformação para o TrackballDecoratorcomo segue:

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

  }

}

Os GetXXXTransform3Dmétodos são métodos de extensão definidos como segue:

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

Segundo , você precisa declarar um Transformpara o seu PerspectiveCameracomo segue:
(o exemplo é retirado de Sasha Barber Elements3D projeto que eu usei para testar isso)

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

, uma vez que estamos indo para armazenar cada parte de toda a transformação em um valor separado, você precisa criar as propriedades relevantes em seu arquivo de configurações, ou seja CameraScaleX, CameraScaleY, CameraScaleZ, CameraTranslateX, CameraTranslateY, CameraTranslateZ, CameraRotateAxisX, CameraRotateAxisY, CameraRotateAxisZe CameraRotateAngle. Todos são do tipo doublee são armazenados no escopo do usuário.

e última etapa é para realmente salvar e carregar essas configurações na câmara usando o seguinte código:

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

Felizmente, eu não esquecer de nada. Se precisar de mais ajuda ou não entender alguma coisa, por favor não hesite em perguntar ;-)

Respondeu 16/03/2011 em 14:08
fonte usuário

votos
0

Eu acredito que o que você precisa é de Posição, LookDirection, UpDirection, fieldOfView, NearPlaneDistance, FarPlaneDistance. Todas as propriedades acima definem a câmara.

Respondeu 15/03/2011 em 10:27
fonte usuário

votos
0

Você precisaria de ambos os dados matriz de câmeras de vista e os dados da matriz de projeção. A matriz de vista conterá os dados sobre a posição, a rotação, a dimensão e a tradução da câmara e da matriz de projecção irá conter coisas como o campo de visão, perto plano, plano de medida e outros dados.

Desculpe eu não posso ajudar com exportação / importação que os dados desde que eu não usei WPF, mas pode haver propriedades RAWDATA exposto se ele usa nada a ver com AS3 é construído em aulas de Matrix, este é usualy um Vector AS3. Opor os as matrizes 16 valores expostos como fileira ordenada valores de ponto flutuante.

Respondeu 09/09/2010 em 00:01
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more