2009-11-25 4 views
2

J'ai une scène 3D WPF où je peux faire un panoramique, tourner et zoomer en utilisant le TrackballDecorator du 3DTools library. Je voudrais enregistrer les paramètres de la caméra (transformation) et être en mesure de les appliquer de nouveau lorsque l'application redémarre la prochaine fois (de sorte que la vue est restaurée).WPF 3D - Comment sauvegarder et charger une vue caméra?

J'ai essayé de sauver chaque valeur individuelle du Camera:

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

Cela ne fonctionne pas, je suppose que ces paramètres ne sont pas mis à jour en fonction des transformations appliquées à la caméra (je reçois le premier toujours valeurs définies dans xaml).

J'ai vérifié la classe Transformation3D mais n'a pas pu trouver un moyen de définir sa valeur ...

Le problème est que les valeurs dois-je obtenir de l'PerspectiveCamera afin de pouvoir restaurer la C'est comme ça que j'ai fermé ma demande la dernière fois. La caméra est réglée sur une position par défaut (en Xaml), puis une transformation est appliquée à cette caméra par le TrackBallDecorator. Comment puis-je sauvegarder cette transformation (quelles valeurs stocker)? Et comment puis-je les réutiliser plus tard?

Répondre

3

Cela va être un peu long, donc garder avec moi ...

1er, vous devez modifier la bibliothèque 3DTools afin que vous puissiez appliquer une transformation à la TrackballDecorator comme suit:

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

    } 

} 

Les méthodes GetXXXTransform3D sont les méthodes d'extension définies comme suit:

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ème, vous devez déclarer un Transform à votre PerspectiveCamera comme suit:
(l'exemple est tiré du projet de Sasha Barber Elements3D que j'ai utilisé pour tester cette)

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

, puisque nous allons pour stocker chaque partie de l'ensemble de la transformation d'une valeur distincte, vous devez créer les propriétés pertinentes dans votre fichier de paramètres, à savoir CameraScaleX, CameraScaleY, CameraScaleZ, CameraTranslateX, CameraTranslateY, CameraTranslateZ, CameraRotateAxisX, CameraRotateAxisY, CameraRotateAxisZ et CameraRotateAngle.Tous sont de type double et sont stockés dans la portée de l'utilisateur.

4ème et dernière étape consiste à enregistrer et charger réellement ces paramètres dans l'appareil en utilisant le code suivant:

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

Si tout va bien, je n'ai rien oublié. Si vous avez besoin de plus d'aide ou ne comprenez pas quelque chose, n'hésitez pas à demander ;-)

+0

Merci pour votre achat! Dans la première étape de la méthode Transform, m_Transform n'a pas .GetScaleTransform3D, .GetTranslateTransform3D ou .GetRotateTransform3D. Sont ces méthodes d'extension? – Michael

+0

@Michael: Oui, ce sont des méthodes d'extension que j'avais oublié d'inclure. Il est maintenant corrigé, merci ;-) –

+1

Une autre chose à noter est que le code ci-dessus ajoute un panoramique (avec le bouton du milieu de la souris) à la bibliothèque 3dtools. Très sympa! – Michael

0

Les caméras doivent afficher les données matricielles et les données de la matrice de projection. La matrice de vue contiendra les données sur la position, la rotation, l'échelle et la translation de la caméra et la matrice de projection contiendra des éléments tels que le champ de vision, le plan proche, le plan lointain et d'autres données. Désolé, je ne peux pas aider à exporter/importer ces données puisque je n'ai pas utilisé WPF, mais il pourrait y avoir des propriétés rawdata exposées si elles utilisent quelque chose à faire avec les classes Matrix de as3, c'est généralement un vecteur as3. Objet les valeurs des matrices 16 exposées sous forme de valeurs à virgule flottante ordonnées par ligne.

0

Je crois que ce dont vous avez besoin est Position, LookDirection, UpDirection, FieldOfView, NearPlaneDistance, FarPlaneDistance. Toutes les propriétés ci-dessus définissent la caméra.