我有一个WPF 3D场景,我可以使用3DTools library中的TrackballDecorator
进行平移,旋转和缩放。我想保存相机设置(转换),并能够在下次重新启动应用程序时重新应用它们(以便恢复视图)。
我尝试保存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将变换应用于此摄像机。如何保存此转换(要存储的值)?我怎样才能在以后重新应用它们?
答案 0 :(得分:4)
这会有点长,所以请耐心等待......
第一,您需要修改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;
}
第二次,您需要向Transform
申报PerspectiveCamera
,如下所示:
(这个例子来自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>
第3次,因为我们要将整个转换的每个部分存储在单独的值中,您需要在设置文件中创建相关属性,即CameraScaleX
,{{ 1}},CameraScaleY
,CameraScaleZ
,CameraTranslateX
,CameraTranslateY
,CameraTranslateZ
,CameraRotateAxisX
,CameraRotateAxisY
和{{1} }。所有类型都为CameraRotateAxisZ
,并存储在用户范围内。
第4次,最后一步是使用以下代码将这些设置实际保存并加载到相机中:
CameraRotateAngle
希望我没有忘记任何事情。如果您需要更多帮助或者不理解某些内容,请不要犹豫; - )
答案 1 :(得分:0)
您需要相机视图矩阵数据和投影矩阵数据。视图矩阵将包含有关摄像机位置,旋转,比例和平移的数据,投影矩阵将包含视野,近平面,远平面和其他数据等内容。
很抱歉,由于我没有使用WPF,我无法帮助导出/导入该数据,但如果它使用as3的内置Matrix类中的任何内容,可能会暴露rawdata属性,这通常是as3 Vector。将矩阵16的值作为行有序浮点值公开。
答案 2 :(得分:0)
我相信你需要的是Position,LookDirection,UpDirection,FieldOfView,NearPlaneDistance,FarPlaneDistance。以上所有属性都定义了相机。