using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using MdxScene.Cameras;
using MdxScene.Shapes;
using MdxWpfInteroperability;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
namespace MdxScene.Controls
{
public class D3dScenePresenter : D3dHost
{
#region inner structures
public enum CameraOperation
{
None,
Zoom,
UniformZoom,
XyMove,
ZMove,
TargetXyRotate,
TargetZRotate,
CameraXyRotate,
CameraZRotate,
ZoomToRegion,
UniformZoomToRegion
}
public class MouseMoveOperation
{
public MouseMoveOperation()
{
Operation = CameraOperation.None;
LeftButtonState = MouseButtonState.Released;
MiddleButtonState = MouseButtonState.Released;
RightButtonState = MouseButtonState.Released;
XButton1State = MouseButtonState.Released;
XButton2State = MouseButtonState.Released;
Modifiers = ModifierKeys.None;
}
#region Properties
public CameraOperation Operation { get; set; }
#region Mouse buttons state
public MouseButtonState LeftButtonState { get; set; }
public MouseButtonState MiddleButtonState { get; set; }
public MouseButtonState RightButtonState { get; set; }
public MouseButtonState XButton1State { get; set; }
public MouseButtonState XButton2State { get; set; }
#endregion
public ModifierKeys Modifiers { get; set; }
#endregion
public bool IsCurrentStateFitting()
{
return IsMouseCurrentStateFitting() && IsKeyboardCurrentStateFitting();
}
public bool IsMouseCurrentStateFitting()
{
return Mouse.LeftButton == LeftButtonState &&
Mouse.MiddleButton == MiddleButtonState &&
Mouse.RightButton == RightButtonState &&
Mouse.XButton1 == XButton1State &&
Mouse.XButton2 == XButton2State;
}
public bool IsKeyboardCurrentStateFitting()
{
ModifierKeys currentModifiers = ModifierKeys.None;
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
currentModifiers |= ModifierKeys.Shift;
}
if(Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
{
currentModifiers |= ModifierKeys.Alt;
}
if(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
currentModifiers |= ModifierKeys.Control;
}
if (Keyboard.IsKeyDown(Key.LWin) || Keyboard.IsKeyDown(Key.RWin))
{
currentModifiers |= ModifierKeys.Windows;
}
return currentModifiers == Modifiers;
}
}
protected class RegionIndicatorAdorner : Adorner
{
public RegionIndicatorAdorner(UIElement adornedElement)
: base(adornedElement)
{
Visibility = Visibility.Collapsed;
IsHitTestVisible = false;
FillBrush = new SolidColorBrush(new Color
{
A = 128,
R = 0,
G = 0,
B = 0
});
BorderPen = new Pen(new SolidColorBrush(new Color
{
A = 128,
R = 255,
G = 255,
B = 255
}), 2)
{
DashStyle = new DashStyle(new double[] {5, 5}, 0)
};
}
#region Properties
public double RectangleTop { get; set; }
public double RectangleLeft { get; set; }
public double RectangleWidth { get; set; }
public double RectangleHeight { get; set; }
public double ActualRectangleTop { get; private set; }
public double ActualRectangleLeft { get; private set; }
#region ActualRectangleWidth
private double _actualRectangleWidth;
public double ActualRectangleWidth { get { return _actualRectangleWidth; } }
#endregion
#region ActualRectangleHeight
private double _actualRectangleHeight;
public double ActualRectangleHeight { get { return _actualRectangleHeight; } }
#endregion
public bool KeepAspectRatio { get; set; }
public Brush FillBrush { get; set; }
public Pen BorderPen { get; set; }
#endregion
protected override void OnRender(DrawingContext drawingContext)
{
_actualRectangleWidth = RectangleWidth;
_actualRectangleHeight = RectangleHeight;
if (KeepAspectRatio)
{
AdjustActualSize(ref _actualRectangleWidth, ref _actualRectangleHeight);
}
ActualRectangleTop = _actualRectangleHeight > 0 ? RectangleTop : RectangleTop + _actualRectangleHeight;
ActualRectangleLeft = _actualRectangleWidth > 0 ? RectangleLeft : RectangleLeft + _actualRectangleWidth;
Width = Math.Abs(_actualRectangleWidth);
Height = Math.Abs(_actualRectangleHeight);
Rect r = new Rect(ActualRectangleLeft, ActualRectangleTop, Width, Height);
drawingContext.DrawRectangle(FillBrush, BorderPen, r);
}
private void AdjustActualSize(ref double actualRectangleWidth, ref double actualRectangleHeight)
{
Rectangle adornedRectangle = AdornedElement as Rectangle;
if (null == adornedRectangle)
{
return;
}
double relativeWidth = Math.Abs(actualRectangleWidth/adornedRectangle.ActualWidth);
double relativeHeight = Math.Abs(actualRectangleHeight/adornedRectangle.ActualHeight);
if (relativeWidth < relativeHeight)
{
actualRectangleHeight *= (relativeWidth/relativeHeight);
}
else
{
actualRectangleWidth *= (relativeHeight/relativeWidth);
}
}
}
#endregion
protected RegionIndicatorAdorner _regionIndicatorAdorner;
#region Constructors
public D3dScenePresenter()
{
InitActions();
}
static D3dScenePresenter()
{
CommandBinding adjustCameraViewBinding =
new CommandBinding(AdjustCameraViewCommand, ExecuteAdjustCameraViewCommand,
CanExecuteAdjustCameraViewCommand);
CommandManager.RegisterClassCommandBinding(typeof (D3dScenePresenter), adjustCameraViewBinding);
CommandBinding horizontalZoomCommandBinding =
new CommandBinding(HorizontalZoomCommand, ExecuteHorizontalZoomCommand, CanExecuteHorizontalZoomCommand);
CommandManager.RegisterClassCommandBinding(typeof (D3dScenePresenter), horizontalZoomCommandBinding);
CommandBinding verticalZoomCommandBinding =
new CommandBinding(VerticalZoomCommand, ExecuteVerticalZoomCommand, CanExecuteVerticalZoomCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), verticalZoomCommandBinding);
CommandBinding uniformZoomCommandBinding =
new CommandBinding(UniformZoomCommand, ExecuteUniformZoomCommand, CanExecuteUniformZoomCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), uniformZoomCommandBinding);
CommandBinding horizontalMoveCommandBinding =
new CommandBinding(HorizontalMoveCommand, ExecuteHorizontalMoveCommand, CanExecuteHorizontalMoveCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), horizontalMoveCommandBinding);
CommandBinding verticalMoveCommandBinding =
new CommandBinding(VerticalMoveCommand, ExecuteVerticalMoveCommand, CanExecuteVerticalMoveCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), verticalMoveCommandBinding);
CommandBinding moveAtLookDirectionCommandBinding =
new CommandBinding(MoveAtLookDirectionCommand, ExecuteMoveAtLookDirectionCommand,
CanExecuteMoveAtLookDirectionCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), moveAtLookDirectionCommandBinding);
CommandBinding rotateCameraAroundXAxisCommandBinding =
new CommandBinding(RotateCameraAroundXAxisCommand, ExecuteRotateCameraAroundXAxisCommand,
CanExecuteRotateCameraAroundXAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateCameraAroundXAxisCommandBinding);
CommandBinding rotateCameraAroundYAxisCommandBinding =
new CommandBinding(RotateCameraAroundYAxisCommand, ExecuteRotateCameraAroundYAxisCommand,
CanExecuteRotateCameraAroundYAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateCameraAroundYAxisCommandBinding);
CommandBinding rotateCameraAroundZAxisCommandBinding =
new CommandBinding(RotateCameraAroundZAxisCommand, ExecuteRotateCameraAroundZAxisCommand,
CanExecuteRotateCameraAroundZAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateCameraAroundZAxisCommandBinding);
CommandBinding rotateTargetAroundXAxisCommandBinding =
new CommandBinding(RotateTargetAroundXAxisCommand, ExecuteRotateTargetAroundXAxisCommand,
CanExecuteRotateTargetAroundXAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateTargetAroundXAxisCommandBinding);
CommandBinding rotateTargetAroundYAxisCommandBinding =
new CommandBinding(RotateTargetAroundYAxisCommand, ExecuteRotateTargetAroundYAxisCommand,
CanExecuteRotateTargetAroundYAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateTargetAroundYAxisCommandBinding);
CommandBinding rotateTargetAroundZAxisCommandBinding =
new CommandBinding(RotateTargetAroundZAxisCommand, ExecuteRotateTargetAroundZAxisCommand,
CanExecuteRotateTargetAroundZAxisCommand);
CommandManager.RegisterClassCommandBinding(typeof(D3dScenePresenter), rotateTargetAroundZAxisCommandBinding);
}
#endregion
protected override void OnLoaded(object sender, RoutedEventArgs e)
{
base.OnLoaded(sender, e);
InvalidateScene();
if (null != D3dRegion)
{
AdornerLayer d3dRegionAdornerLayer = AdornerLayer.GetAdornerLayer(D3dRegion);
if (null != d3dRegionAdornerLayer)
{
_regionIndicatorAdorner = new RegionIndicatorAdorner(D3dRegion)
{
FillBrush = RegionIndicatorFillBrush,
BorderPen = RegionIndicatorBorderPen
};
d3dRegionAdornerLayer.Add(_regionIndicatorAdorner);
}
}
}
protected override void OnD3dDeviceFailure(Exception ex)
{
base.OnD3dDeviceFailure(ex);
if (IsLoaded)
{
InvalidateScene();
}
}
#region InitActions
protected Point _lastSurfaceMousePosition;
private void InitActions()
{
D3dSurfaceMouseMove += OnD3dSurfaceMouseMove;
D3dSurfaceMouseEnter += OnD3dSurfaceMouseEnter;
D3dSurfaceMouseLeave += OnD3dSurfaceMouseLeave;
D3dSurfaceMouseDown += OnD3dSurfaceMouseDown;
D3dSurfaceMouseUp += OnD3dSurfaceMouseUp;
// Add default mouse-move operations
lock (MouseMoveOperations)
{
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.XyMove,
LeftButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Shift
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.TargetZRotate,
LeftButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Alt
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.CameraZRotate,
LeftButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Alt | ModifierKeys.Control
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.ZMove,
RightButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Shift
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.TargetXyRotate,
RightButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Alt
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.CameraXyRotate,
RightButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Alt | ModifierKeys.Control
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.Zoom,
MiddleButtonState = MouseButtonState.Pressed
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.UniformZoom,
MiddleButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Shift
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.ZoomToRegion,
RightButtonState = MouseButtonState.Pressed,
LeftButtonState = MouseButtonState.Pressed
});
MouseMoveOperations.Add(new MouseMoveOperation
{
Operation = CameraOperation.UniformZoomToRegion,
RightButtonState = MouseButtonState.Pressed,
LeftButtonState = MouseButtonState.Pressed,
Modifiers = ModifierKeys.Shift
});
}
}
void OnD3dSurfaceMouseDown(object sender, D3dSurfaceMouseButtonEventArgs e)
{
HandleZoomToRegion();
}
void OnD3dSurfaceMouseUp(object sender, D3dSurfaceMouseButtonEventArgs e)
{
HandleZoomToRegion();
}
private void OnD3dSurfaceMouseEnter(object sender, D3dSurfaceMouseEventArgs e)
{
_lastSurfaceMousePosition = e.D3dSurfaceMousePosition;
// Pick the mouse-over shape, if needed.
if (_isMouseOverShapeTestEnabled)
{
InvalidateCurrentMouseOverShape();
}
}
private void OnD3dSurfaceMouseMove(object sender, D3dSurfaceMouseEventArgs e)
{
HandleZoomToRegion();
List<CameraOperation> cameraOperations;
lock (MouseMoveOperations)
{
cameraOperations =
MouseMoveOperations.Where(o => o.IsCurrentStateFitting()).Select(o => o.Operation).Distinct().ToList();
}
if (cameraOperations.Any())
{
Point currentSurfacePosition = e.D3dSurfaceMousePosition;
Vector delta = currentSurfacePosition - _lastSurfaceMousePosition;
float relativeDeltaX = (float)(delta.X / D3dSurfaceWidth);
float relativeDeltaY = (float)(delta.Y / D3dSurfaceHeight);
foreach (CameraOperation operation in cameraOperations)
{
PerformOperation(operation, relativeDeltaX, relativeDeltaY);
}
if (_isInZoomToRegionMode && null != _regionIndicatorAdorner)
{
_regionIndicatorAdorner.RectangleWidth += delta.X * (D3dRegionActualWidth / D3dSurfaceWidth);
_regionIndicatorAdorner.RectangleHeight += delta.Y * (D3dRegionActualHeight / D3dSurfaceHeight);
_regionIndicatorAdorner.InvalidateVisual();
}
}
else
{
// Pick the mouse-over shape, if needed.
if (_isMouseOverShapeTestEnabled)
{
InvalidateCurrentMouseOverShape();
}
}
_lastSurfaceMousePosition = e.D3dSurfaceMousePosition;
}
private void OnD3dSurfaceMouseLeave(object sender, D3dSurfaceMouseEventArgs e)
{
_lastSurfaceMousePosition = new Point(-1, -1);
// Pick the mouse-over shape, if needed.
if (_isMouseOverShapeTestEnabled)
{
InvalidateCurrentMouseOverShape();
}
}
#endregion
#region HandleZoomToRegion
protected bool _isInZoomToRegionMode = false;
protected void HandleZoomToRegion()
{
// Get the current ZoomToRegion operation state
CameraOperation zoomToRegionOperation = CameraOperation.None;
lock (MouseMoveOperations)
{
List<CameraOperation> cameraOperations =
MouseMoveOperations.Where(o => (CameraOperation.ZoomToRegion == o.Operation || CameraOperation.UniformZoomToRegion == o.Operation)
&& o.IsCurrentStateFitting()).
Select(o => o.Operation).Distinct().ToList();
if (cameraOperations.Any())
{
zoomToRegionOperation = cameraOperations.First();
}
}
if (CameraOperation.None == zoomToRegionOperation)
{
if (_isInZoomToRegionMode)
{
// The region's selection operation has just ended...
ApplyZoomToRegion();
}
}
else
{
// We are in ZoomToRegion state...
if (!_isInZoomToRegionMode)
{
// The region's selection operation has just started...
StartZoomToRegion();
}
else
{
// We are in a middle of a region's selection operation...
if (null != _regionIndicatorAdorner)
{
_regionIndicatorAdorner.KeepAspectRatio = CameraOperation.UniformZoomToRegion ==
zoomToRegionOperation;
}
}
}
}
private void StartZoomToRegion()
{
if (null == _regionIndicatorAdorner)
{
return;
}
_regionIndicatorAdorner.RectangleTop = _lastSurfaceMousePosition.Y *
(D3dRegionActualHeight / D3dSurfaceHeight);
_regionIndicatorAdorner.RectangleLeft = _lastSurfaceMousePosition.X *
(D3dRegionActualWidth / D3dSurfaceWidth);
_regionIndicatorAdorner.RectangleWidth = 0;
_regionIndicatorAdorner.RectangleHeight = 0;
_regionIndicatorAdorner.Visibility = Visibility.Visible;
_regionIndicatorAdorner.InvalidateVisual();
_isInZoomToRegionMode = true;
}
private void ApplyZoomToRegion()
{
if (null == _regionIndicatorAdorner)
{
return;
}
if (Math.Abs(_regionIndicatorAdorner.ActualRectangleHeight) > 0 &&
Math.Abs(_regionIndicatorAdorner.ActualRectangleWidth) > 0)
{
float selectedRegionLeft =
(float) (_regionIndicatorAdorner.ActualRectangleLeft/(D3dRegionActualWidth/2) - 1);
float selectedRegionTop =
(float) (1 - _regionIndicatorAdorner.ActualRectangleTop/(D3dRegionActualHeight/2));
float selectedRegionRight =
(float) (selectedRegionLeft + _regionIndicatorAdorner.Width/(D3dRegionActualWidth/2));
float selectedRegionBottom =
(float) (selectedRegionTop - _regionIndicatorAdorner.Height/(D3dRegionActualHeight/2));
PerformSceneAction(() => CurrentScene.Camera.ZoomToProjectionRegion(
selectedRegionLeft, selectedRegionTop, selectedRegionRight,
selectedRegionBottom, false));
}
_regionIndicatorAdorner.Visibility = Visibility.Collapsed;
_regionIndicatorAdorner.InvalidateVisual();
_isInZoomToRegionMode = false;
}
#endregion
#region PerformOperation
protected List<Action> _pendingActions = new List<Action>();
private void PerformOperation(CameraOperation operation, float relativeDeltaX, float relativeDeltaY)
{
switch (operation)
{
case CameraOperation.Zoom:
PerformSceneAction(() => PerformZoom(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.UniformZoom:
PerformSceneAction(() => PerformUniformZoom(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.XyMove:
PerformSceneAction(() => PerformXyMove(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.ZMove:
PerformSceneAction(() => PerformZMove(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.TargetXyRotate:
PerformSceneAction(() => PerformTargetXyRotate(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.TargetZRotate:
AdjustZRotateDelta(ref relativeDeltaX, ref relativeDeltaY);
PerformSceneAction(() => PerformTargetZRotate(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.CameraXyRotate:
PerformSceneAction(() => PerformCameraXyRotate(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.CameraZRotate:
AdjustZRotateDelta(ref relativeDeltaX, ref relativeDeltaY);
PerformSceneAction(() => PerformCameraZRotate(relativeDeltaX, relativeDeltaY));
break;
case CameraOperation.ZoomToRegion:
case CameraOperation.UniformZoomToRegion:
HandleZoomToRegion();
break;
}
}
public void PerformSceneAction(Action a)
{
AddPendingAction(a);
InvalidateScene();
}
public void AddPendingAction(Action a)
{
if (null == a)
{
return;
}
lock (_pendingActions)
{
_pendingActions.Add(a);
}
}
protected void PerformXyMove(float relativeDeltaX, float relativeDeltaY)
{
CurrentScene.Camera.RelativeXyMove(-relativeDeltaX * 2, relativeDeltaY * 2);
}
protected void PerformZMove(float relativeDeltaX, float relativeDeltaY)
{
float relativeDeltaZ = Math.Abs(relativeDeltaX) > Math.Abs(relativeDeltaY)
? relativeDeltaX
: -relativeDeltaY;
CurrentScene.Camera.RelativeZMove(relativeDeltaZ);
}
protected void PerformZoom(float relativeDeltaX, float relativeDeltaY)
{
CurrentScene.Camera.Zoom(1 - relativeDeltaX, 1 + relativeDeltaY);
}
protected void PerformUniformZoom(float relativeDeltaX, float relativeDeltaY)
{
float relativeZoomDelta = Math.Abs(relativeDeltaX) > Math.Abs(relativeDeltaY)
? relativeDeltaX
: -relativeDeltaY;
PerformZoom(relativeZoomDelta, -relativeZoomDelta);
}
protected void PerformTargetZRotate(float relativeDeltaX, float relativeDeltaY)
{
float relativeArcLength = relativeDeltaX + relativeDeltaY;
CurrentScene.Camera.RelativeRotateZ(relativeArcLength, CameraTransformationCenterPosition.CameraPosition, CameraRotationDirection.Clockwise);
}
protected void PerformTargetXyRotate(float relativeDeltaX, float relativeDeltaY)
{
CurrentScene.Camera.RelativeRotateX(relativeDeltaY/8, CameraTransformationCenterPosition.CameraPosition, CameraRotationDirection.CounterClockwise);
CurrentScene.Camera.RelativeRotateY(relativeDeltaX/8, CameraTransformationCenterPosition.CameraPosition, CameraRotationDirection.CounterClockwise);
}
protected void PerformCameraZRotate(float relativeDeltaX, float relativeDeltaY)
{
float relativeArcLength = relativeDeltaX + relativeDeltaY;
CurrentScene.Camera.RelativeRotateZ(relativeArcLength, CameraTransformationCenterPosition.TargetPosition, CameraRotationDirection.Clockwise);
}
protected void PerformCameraXyRotate(float relativeDeltaX, float relativeDeltaY)
{
CurrentScene.Camera.RelativeRotateX(relativeDeltaY, CameraTransformationCenterPosition.TargetPosition, CameraRotationDirection.CounterClockwise);
CurrentScene.Camera.RelativeRotateY(relativeDeltaX, CameraTransformationCenterPosition.TargetPosition, CameraRotationDirection.CounterClockwise);
}
private void AdjustZRotateDelta(ref float relativeDeltaX, ref float relativeDeltaY)
{
relativeDeltaX /= (D3dSurfaceHeight / 2) > _lastSurfaceMousePosition.Y ? 4 : -4;
relativeDeltaY /= (D3dSurfaceWidth / 2) < _lastSurfaceMousePosition.X ? 4 : -4;
}
#endregion
public void InvalidateScene()
{
if (null == CurrentScene)
{
return;
}
// Start the thread that renders the scene, if it is needed.
if (_renderSceneThread == null)
{
StartRenderSceneThread();
}
// Indicate that the scene render is invalid.
_renderSceneEvent.Set();
}
#region BeginSceneUpdate & EndSceneUpdate
private readonly object _sceneUpdateLocker = new object();
public void BeginSceneUpdate()
{
Monitor.Enter(_sceneUpdateLocker);
}
public void EndSceneUpdate()
{
Monitor.Exit(_sceneUpdateLocker);
// Present the scene.
InvalidateScene();
}
#endregion
#region RenderScene
private Thread _renderSceneThread = null;
private bool _continueRenderSceneThread;
private AutoResetEvent _renderSceneEvent = new AutoResetEvent(false);
protected void RenderScene()
{
if (null == CurrentScene)
{
return;
}
List<Action> currentActions = new List<Action>();
lock (_pendingActions)
{
currentActions.AddRange(_pendingActions);
_pendingActions.Clear();
}
try
{
BeginDrawing();
Monitor.Enter(_sceneUpdateLocker);
// Perform scene actions.
currentActions.ForEach(a => a());
// Render the scene.
CurrentScene.Render(D3dDevice);
// Pick the mouse-over shape, if needed.
if (_isMouseOverShapeTestEnabled)
{
InvalidateCurrentMouseOverShape();
}
}
catch
{
}
finally
{
Monitor.Exit(_sceneUpdateLocker);
EndDrawing();
}
}
private void StartRenderSceneThread()
{
if (null == _renderSceneThread)
{
_continueRenderSceneThread = true;
_renderSceneThread = new Thread(new ThreadStart(() =>
{
while (_continueRenderSceneThread)
{
_renderSceneEvent.WaitOne();
if (_continueRenderSceneThread)
{
RenderScene();
}
}
}));
_renderSceneThread.Start();
}
}
private void StopRenderSceneThread()
{
if (_renderSceneThread != null)
{
_continueRenderSceneThread = false;
_renderSceneEvent.Set();
_renderSceneThread.Join();
_renderSceneThread = null;
}
}
#endregion
#region Pick
private Thread _pickThread = null;
private bool _continuePickThread;
private AutoResetEvent _pickEvent = new AutoResetEvent(false);
protected void UpdateCurrentMouseOverShape()
{
if (null == CurrentScene)
{
return;
}
Monitor.Enter(_sceneUpdateLocker);
try
{
D3dShape currShape = (0 <= _lastSurfaceMousePosition.X && 0 <= _lastSurfaceMousePosition.Y)
? Pick(_lastSurfaceMousePosition.X, _lastSurfaceMousePosition.Y)
: null;
CurrentMouseOverShape = currShape;
}
catch
{
}
finally
{
Monitor.Exit(_sceneUpdateLocker);
}
}
private void StartPickThread()
{
if (null == _pickThread)
{
_continuePickThread = true;
_pickThread = new Thread(new ThreadStart(() =>
{
while (_continuePickThread)
{
_pickEvent.WaitOne();
if (_continuePickThread)
{
UpdateCurrentMouseOverShape();
}
}
}));
_pickThread.Start();
}
}
private void StopPickThread()
{
if (_pickThread != null)
{
_continuePickThread = false;
_pickEvent.Set();
_pickThread.Join();
_pickThread = null;
}
}
public D3dShape Pick(double surfaceX, double surfaceY)
{
if (null == CurrentScene)
{
return null;
}
return CurrentScene.Pick(D3dDevice, (float) surfaceX, (float) surfaceY);
}
public void InvalidateCurrentMouseOverShape()
{
if (null == CurrentScene)
{
return;
}
// Start the thread that renders the scene, if it is needed.
if (_pickThread == null)
{
StartPickThread();
}
// Indicate that the scene render is invalid.
_pickEvent.Set();
}
#endregion
#region Properties
#region Scene
public D3dScene Scene
{
get { return (D3dScene)GetValue(SceneProperty); }
set { SetValue(SceneProperty, value); }
}
public static readonly DependencyProperty SceneProperty =
DependencyProperty.Register("Scene", typeof(D3dScene), typeof(D3dScenePresenter),
new UIPropertyMetadata(null, OnSceneChanged));
private static void OnSceneChanged(DependencyObject o, DependencyPropertyChangedEventArgs arg)
{
D3dScenePresenter dsp = o as D3dScenePresenter;
if (null==dsp)
{
return;
}
// We can access a dependency-property only in the UI thread.
// So, store the value in another property, for let accessing in other threads.
dsp.CurrentScene = dsp.Scene;
}
protected D3dScene CurrentScene { get; private set; }
#endregion
#region MouseMoveOperations
private List<MouseMoveOperation> _mouseMoveOperations;
public List<MouseMoveOperation> MouseMoveOperations
{
get { return _mouseMoveOperations ?? (_mouseMoveOperations = new List<MouseMoveOperation>()); }
}
#endregion
#region IsMouseOverShapeTestEnabled
public bool IsMouseOverShapeTestEnabled
{
get { return (bool)GetValue(IsMouseOverShapeTestEnabledProperty); }
set { SetValue(IsMouseOverShapeTestEnabledProperty, value); }
}
public static readonly DependencyProperty IsMouseOverShapeTestEnabledProperty =
DependencyProperty.Register("IsMouseOverShapeTestEnabled", typeof(bool), typeof(D3dScenePresenter),
new UIPropertyMetadata(false, OnIsMouseOverShapeTestEnabledChanged));
private static void OnIsMouseOverShapeTestEnabledChanged(DependencyObject o, DependencyPropertyChangedEventArgs arg)
{
D3dScenePresenter dsp = o as D3dScenePresenter;
if (null == dsp)
{
return;
}
// We can access a dependency-property only in the UI thread.
// So, store the value in another property, for let accessing in other threads.
dsp._isMouseOverShapeTestEnabled = dsp.IsMouseOverShapeTestEnabled;
}
private bool _isMouseOverShapeTestEnabled;
#endregion
#region CurrentMouseOverShape
private D3dShape _currentMouseOverShape;
public D3dShape CurrentMouseOverShape
{
get { return _currentMouseOverShape; }
protected set
{
if (value != _currentMouseOverShape)
{
D3dShape oldValue = _currentMouseOverShape;
_currentMouseOverShape = value;
// Raise MouseOverShapeChanged event
Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new ThreadStart(() =>
{
MouseOverShapeChangedRoutedEventArgs arg =
new MouseOverShapeChangedRoutedEventArgs(
D3dScenePresenter.MouseOverShapeChangedEvent)
{
OldShape = oldValue,
NewShape = _currentMouseOverShape
};
RaiseEvent(arg);
}));
}
}
}
#endregion
#region RegionIndicatorFillBrush
public Brush RegionIndicatorFillBrush
{
get { return (Brush)GetValue(RegionIndicatorFillBrushProperty); }
set { SetValue(RegionIndicatorFillBrushProperty, value); }
}
public static readonly DependencyProperty RegionIndicatorFillBrushProperty =
DependencyProperty.Register("RegionIndicatorFillBrush", typeof (Brush), typeof (D3dScenePresenter),
new UIPropertyMetadata(new SolidColorBrush(new Color
{
A = 128,
R = 0,
G = 0,
B = 0
}), OnRegionIndicatorFillBrushChanged));
private static void OnRegionIndicatorFillBrushChanged(DependencyObject o, DependencyPropertyChangedEventArgs arg)
{
D3dScenePresenter dsp = o as D3dScenePresenter;
if (null == dsp)
{
return;
}
if (null != dsp._regionIndicatorAdorner)
{
dsp._regionIndicatorAdorner.FillBrush = dsp.RegionIndicatorFillBrush;
}
}
#endregion
#region RegionIndicatorBorderPen
public Pen RegionIndicatorBorderPen
{
get { return (Pen)GetValue(RegionIndicatorBorderPenProperty); }
set { SetValue(RegionIndicatorBorderPenProperty, value); }
}
public static readonly DependencyProperty RegionIndicatorBorderPenProperty =
DependencyProperty.Register("RegionIndicatorBorderPen", typeof (Pen), typeof (D3dScenePresenter),
new UIPropertyMetadata(new Pen(new SolidColorBrush(new Color
{
A = 128,
R = 255,
G = 255,
B = 255
}), 2)
{
DashStyle = new DashStyle(new double[] {5, 5}, 0)
}, OnRegionIndicatorBorderPenChanged));
private static void OnRegionIndicatorBorderPenChanged(DependencyObject o, DependencyPropertyChangedEventArgs arg)
{
D3dScenePresenter dsp = o as D3dScenePresenter;
if (null == dsp)
{
return;
}
if (null != dsp._regionIndicatorAdorner)
{
dsp._regionIndicatorAdorner.BorderPen = dsp.RegionIndicatorBorderPen;
}
}
#endregion
#endregion
#region Commands
#region AdjustCameraViewCommand
private static RoutedCommand _adjustCameraViewCommand;
public static RoutedCommand AdjustCameraViewCommand
{
get
{
return _adjustCameraViewCommand ??
(_adjustCameraViewCommand =
new RoutedCommand("AdjustCameraView", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteAdjustCameraViewCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteAdjustCameraViewCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
dsp.AdjustCameraView();
}
protected void AdjustCameraView()
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.AdjustCameraView();
}
});
}
#endregion
#region HorizontalZoomCommand
private static RoutedCommand _horizontalZoomCommand;
public static RoutedCommand HorizontalZoomCommand
{
get
{
return _horizontalZoomCommand ??
(_horizontalZoomCommand =
new RoutedCommand("HorizontalZoom", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteHorizontalZoomCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteHorizontalZoomCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float scalingFactor = 1;
float.TryParse(e.Parameter.ToString(), out scalingFactor);
dsp.HorizontalZoom(scalingFactor);
}
protected void HorizontalZoom(float scalingFactor)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.Zoom(scalingFactor, 1);
}
});
}
#endregion
#region VerticalZoomCommand
private static RoutedCommand _verticalZoomCommand;
public static RoutedCommand VerticalZoomCommand
{
get
{
return _verticalZoomCommand ??
(_verticalZoomCommand =
new RoutedCommand("VerticalZoom", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteVerticalZoomCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteVerticalZoomCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float scalingFactor = 1;
float.TryParse(e.Parameter.ToString(), out scalingFactor);
dsp.VerticalZoom(scalingFactor);
}
protected void VerticalZoom(float scalingFactor)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.Zoom(1, scalingFactor);
}
});
}
#endregion
#region UniformZoomCommand
private static RoutedCommand _uniformZoomCommand;
public static RoutedCommand UniformZoomCommand
{
get
{
return _uniformZoomCommand ??
(_uniformZoomCommand =
new RoutedCommand("UniformZoom", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteUniformZoomCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteUniformZoomCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float scalingFactor = 1;
float.TryParse(e.Parameter.ToString(), out scalingFactor);
dsp.UniformZoom(scalingFactor);
}
protected void UniformZoom(float scalingFactor)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.Zoom(scalingFactor, scalingFactor);
}
});
}
#endregion
#region HorizontalMoveCommand
private static RoutedCommand _horizontalMoveCommand;
public static RoutedCommand HorizontalMoveCommand
{
get
{
return _horizontalMoveCommand ??
(_horizontalMoveCommand =
new RoutedCommand("HorizontalMove", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteHorizontalMoveCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteHorizontalMoveCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float relativeDelta = 0;
float.TryParse(e.Parameter.ToString(), out relativeDelta);
dsp.HorizontalMove(relativeDelta);
}
protected void HorizontalMove(float relativeDelta)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeXyMove(relativeDelta, 0);
}
});
}
#endregion
#region VerticalMoveCommand
private static RoutedCommand _verticalMoveCommand;
public static RoutedCommand VerticalMoveCommand
{
get
{
return _verticalMoveCommand ??
(_verticalMoveCommand =
new RoutedCommand("VerticalMove", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteVerticalMoveCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteVerticalMoveCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float relativeDelta = 0;
float.TryParse(e.Parameter.ToString(), out relativeDelta);
dsp.VerticalMove(relativeDelta);
}
protected void VerticalMove(float relativeDelta)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeXyMove(0, relativeDelta);
}
});
}
#endregion
#region MoveAtLookDirectionCommand
private static RoutedCommand _moveAtLookDirectionCommand;
public static RoutedCommand MoveAtLookDirectionCommand
{
get
{
return _moveAtLookDirectionCommand ??
(_moveAtLookDirectionCommand =
new RoutedCommand("MoveAtLookDirection", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteMoveAtLookDirectionCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteMoveAtLookDirectionCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float relativeDelta = 0;
float.TryParse(e.Parameter.ToString(), out relativeDelta);
dsp.MoveAtLookDirection(relativeDelta);
}
protected void MoveAtLookDirection(float relativeDelta)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeZMove(relativeDelta);
}
});
}
#endregion
#region RotateCameraAroundXAxisCommand
private static RoutedCommand _rotateCameraAroundXAxisCommand;
public static RoutedCommand RotateCameraAroundXAxisCommand
{
get
{
return _rotateCameraAroundXAxisCommand ??
(_rotateCameraAroundXAxisCommand =
new RoutedCommand("RotateCameraAroundXAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateCameraAroundXAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateCameraAroundXAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateCameraAroundXAxis(radians);
}
protected void RotateCameraAroundXAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateX(radians, CameraTransformationCenterPosition.TargetPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#region RotateCameraAroundYAxisCommand
private static RoutedCommand _rotateCameraAroundYAxisCommand;
public static RoutedCommand RotateCameraAroundYAxisCommand
{
get
{
return _rotateCameraAroundYAxisCommand ??
(_rotateCameraAroundYAxisCommand =
new RoutedCommand("RotateCameraAroundYAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateCameraAroundYAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateCameraAroundYAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateCameraAroundYAxis(radians);
}
protected void RotateCameraAroundYAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateY(radians, CameraTransformationCenterPosition.TargetPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#region RotateCameraAroundZAxisCommand
private static RoutedCommand _rotateCameraAroundZAxisCommand;
public static RoutedCommand RotateCameraAroundZAxisCommand
{
get
{
return _rotateCameraAroundZAxisCommand ??
(_rotateCameraAroundZAxisCommand =
new RoutedCommand("RotateCameraAroundZAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateCameraAroundZAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateCameraAroundZAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateCameraAroundZAxis(radians);
}
protected void RotateCameraAroundZAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateZ(radians, CameraTransformationCenterPosition.TargetPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#region RotateTargetAroundXAxisCommand
private static RoutedCommand _rotateTargetAroundXAxisCommand;
public static RoutedCommand RotateTargetAroundXAxisCommand
{
get
{
return _rotateTargetAroundXAxisCommand ??
(_rotateTargetAroundXAxisCommand =
new RoutedCommand("RotateTargetAroundXAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateTargetAroundXAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateTargetAroundXAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateTargetAroundXAxis(radians);
}
protected void RotateTargetAroundXAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateX(radians, CameraTransformationCenterPosition.CameraPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#region RotateTargetAroundYAxisCommand
private static RoutedCommand _rotateTargetAroundYAxisCommand;
public static RoutedCommand RotateTargetAroundYAxisCommand
{
get
{
return _rotateTargetAroundYAxisCommand ??
(_rotateTargetAroundYAxisCommand =
new RoutedCommand("RotateTargetAroundYAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateTargetAroundYAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateTargetAroundYAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateTargetAroundYAxis(radians);
}
protected void RotateTargetAroundYAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateY(radians, CameraTransformationCenterPosition.CameraPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#region RotateTargetAroundZAxisCommand
private static RoutedCommand _rotateTargetAroundZAxisCommand;
public static RoutedCommand RotateTargetAroundZAxisCommand
{
get
{
return _rotateTargetAroundZAxisCommand ??
(_rotateTargetAroundZAxisCommand =
new RoutedCommand("RotateTargetAroundZAxis", typeof(D3dScenePresenter)));
}
}
protected static void CanExecuteRotateTargetAroundZAxisCommand(object sender, CanExecuteRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
e.CanExecute = true;
}
protected static void ExecuteRotateTargetAroundZAxisCommand(object sender, ExecutedRoutedEventArgs e)
{
D3dScenePresenter dsp = sender as D3dScenePresenter;
if (null == dsp)
{
return;
}
float radians = 0;
float.TryParse(e.Parameter.ToString(), out radians);
dsp.RotateTargetAroundZAxis(radians);
}
protected void RotateTargetAroundZAxis(float radians)
{
PerformSceneAction(() =>
{
if (null != CurrentScene)
{
CurrentScene.Camera.RelativeRotateZ(radians, CameraTransformationCenterPosition.CameraPosition,
CameraRotationDirection.Clockwise);
}
});
}
#endregion
#endregion
#region Events
#region MouseOverShapeChanged
public static readonly RoutedEvent MouseOverShapeChangedEvent = EventManager.RegisterRoutedEvent(
"MouseOverShapeChanged", RoutingStrategy.Bubble, typeof (MouseOverShapeChangedRoutedEventHandler),
typeof (D3dScenePresenter));
public event MouseOverShapeChangedRoutedEventHandler MouseOverShapeChanged
{
add { AddHandler(MouseOverShapeChangedEvent, value); }
remove { RemoveHandler(MouseOverShapeChangedEvent, value); }
}
#endregion
#endregion
#region IDisposable implementation
public override void Dispose()
{
StopRenderSceneThread();
StopPickThread();
base.Dispose();
}
#endregion
}
}