using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Aiwaz.Resources;
using System.Windows.Threading;
using Aiwaz.Core;
using Aiwaz.Contracts;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using SlimDX;
namespace Aiwaz.Editor.Controls
{
///
/// Interaction logic for AiwazViewControl.xaml
///
public partial class AiwazViewControl : System.Windows.Controls.UserControl
{
#region Win32
public static Point CorrectGetPosition(Visual relativeTo)
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return relativeTo.PointFromScreen(new Point(w32Mouse.X, w32Mouse.Y));
}
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
#endregion
private DateTime lastPerformCalledTime = DateTime.UtcNow;
private bool rightButtonPressed;
private bool leftButtonPressed;
private bool moveThresholdReached;
private Point lastMousePosition;
private int desiredFPS = 30;
private DispatcherTimer renderTimer;
private RenderCommandNode mainRootNode;
private RenderCommandNode rootNode;
private Shader selectionShader;
private RenderCommandNode selectionMarkerNode;
private PickableResourceInfo selectedResourceInfo;
public delegate void PerformCalledDelegate(object sender, TimeSpan elapsedTime);
public delegate void SelectedResourceChangedDelegate(object sender, PickableResourceInfo newSelectedResourceInfo);
public event PerformCalledDelegate PerformCalled;
public event SelectedResourceChangedDelegate SelectedResourceChanged;
public AiwazViewControl()
{
InitializeComponent();
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
return;
this.SwapChain = new SwapChain(RenderTarget.Handle);
this.SwapChain.HasDepthStencilBuffer = true;
MainRootNode.Children.Add(this.SwapChain);
MainRootNode.Children.Add(RootNode);
renderTimer = new DispatcherTimer();
renderTimer.Tick += new EventHandler(delegate(object s, EventArgs a)
{
this.Perform();
});
renderTimer.Interval = TimeSpan.FromMilliseconds(1000 / 30.0);
renderTimer.Start();
selectionMarkerNode = new RenderCommandNode();
selectionShader = new Shader(new ShaderParams() { FileName = "Resources/SelectionHighlight.fx" });
selectionShader.Priority = 128;
MainRootNode.Children.Add(selectionMarkerNode);
this.RenderTarget.MouseDown += new System.Windows.Forms.MouseEventHandler(RenderTarget_MouseButtonDown);
this.RenderTarget.MouseUp += new System.Windows.Forms.MouseEventHandler(RenderTarget_MouseButtonUp);
}
public ICamera MainCamera
{
get
{
return FindLastAvailableCamera(MainRootNode);
}
}
public PickableResourcesDataSource PickableResources { get; set; }
public RenderCommandNode MainRootNode
{
get
{
if (mainRootNode == null)
{
mainRootNode = new RenderCommandNode();
}
return mainRootNode;
}
set
{
mainRootNode = value;
}
}
public SwapChain SwapChain { get; protected set; }
public RenderCommandNode RootNode
{
get
{
if (rootNode == null)
{
rootNode = new RenderCommandNode();
}
return rootNode;
}
set
{
mainRootNode.Children.Remove(rootNode);
rootNode = value;
mainRootNode.Children.Add(value);
}
}
public TimeSpan RenderInterval
{
get { return renderTimer.Interval; }
set { renderTimer.Interval = value; }
}
public PickableResourceInfo SelectedResourceInfo
{
get
{
return selectedResourceInfo;
}
set
{
if (selectedResourceInfo != null)
{
selectionMarkerNode.Children.Clear();
selectionMarkerNode.MarkDirty();
}
selectedResourceInfo = value;
if (selectedResourceInfo != null)
{
selectionMarkerNode.Children.Add(selectionShader);
if (selectedResourceInfo.Transformation != null)
selectionMarkerNode.Children.Add(selectedResourceInfo.Transformation);
if (selectedResourceInfo.Resource != null)
selectionMarkerNode.Children.Add(selectedResourceInfo.Resource);
}
if (SelectedResourceChanged != null)
SelectedResourceChanged(this, selectedResourceInfo);
}
}
public int DesiredFPS
{
get
{
return desiredFPS;
}
set
{
desiredFPS = value;
if (desiredFPS <= 0)
renderTimer.Interval = TimeSpan.FromMilliseconds(0);
else
renderTimer.Interval = TimeSpan.FromMilliseconds(1000 / (double)desiredFPS);
}
}
private ICamera FindLastAvailableCamera(Resource rootNode)
{
ICamera lastFoundCamera = null;
if (rootNode.Children != null)
foreach (var child in rootNode.Children)
{
if (child is ICamera)
lastFoundCamera = child as ICamera;
if (child is Resource)
lastFoundCamera = this.FindLastAvailableCamera(child as Resource) ?? lastFoundCamera;
}
return lastFoundCamera;
}
protected void Perform()
{
mainRootNode.Update(false);
mainRootNode.ProcessCommands();
this.SwapChain.Present();
var deltaTime = DateTime.UtcNow - lastPerformCalledTime;
this.PerformCalledInternal(deltaTime);
if (PerformCalled != null)
PerformCalled(this, deltaTime);
lastPerformCalledTime = DateTime.UtcNow;
}
private void RenderTarget_Resize(object sender, EventArgs e)
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
return;
this.SwapChain.Resize((int)this.Width, (int)this.Height);
}
private void RenderTarget_MouseButtonUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
rightButtonPressed = false;
else if (e.Button == MouseButtons.Left)
leftButtonPressed = false;
lastMousePosition = CorrectGetPosition(this);
if (!moveThresholdReached)
{
if (e.Button == MouseButtons.Right)
{
if (this.ContextMenu != null)
this.ContextMenu.IsOpen = true;
}
else if (e.Button == MouseButtons.Left)
{
if (PickableResources != null)
{
var mainCamera = this.MainCamera;
int width = this.SwapChain.ViewPort.Width;
int height = this.SwapChain.ViewPort.Height;
var viewProj = mainCamera.ViewMatrix * mainCamera.ProjectionMatrix;
Vector3 ZNearPlane = Vector3.Unproject(new Vector3((float)lastMousePosition.X, (float)lastMousePosition.Y, 0), 0, 0, width, height,
this.SwapChain.ViewPort.MinDepth, this.SwapChain.ViewPort.MaxDepth, viewProj);
Vector3 ZFarPlane = Vector3.Unproject(new Vector3((float)lastMousePosition.X, (float)lastMousePosition.Y, 1), 0, 0, width, height,
this.SwapChain.ViewPort.MinDepth, this.SwapChain.ViewPort.MaxDepth, viewProj);
Vector3 direction = ZFarPlane - ZNearPlane;
direction.Normalize();
Ray ray = new Ray(ZNearPlane, direction);
PickableResourceInfo winnerResourceInfo = this.SelectedResourceInfo;
float distance = float.MaxValue;
foreach (var pickable in PickableResources)
{
float newDistance;
if (pickable.PickHull.TryPick(ray, pickable.Transformation.WorldMatrix, out newDistance)
&& newDistance < distance)
{
winnerResourceInfo = pickable;
distance = newDistance;
}
}
if (this.SelectedResourceInfo != null && winnerResourceInfo != null && this.SelectedResourceInfo.Resource == winnerResourceInfo.Resource)
winnerResourceInfo = null;
this.SelectedResourceInfo = winnerResourceInfo;
}
}
}
if (!leftButtonPressed && !rightButtonPressed)
{
renderTimer.Interval = TimeSpan.FromMilliseconds(1000 / (double)desiredFPS);
moveThresholdReached = false;
}
}
private void RenderTarget_MouseButtonDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
rightButtonPressed = true;
else if (e.Button == MouseButtons.Left)
leftButtonPressed = true;
if (leftButtonPressed || rightButtonPressed)
renderTimer.Interval = TimeSpan.FromMilliseconds(0);
lastMousePosition = CorrectGetPosition(this);
}
private void PerformCalledInternal(TimeSpan elapsedTime)
{
var currentMousePosition = CorrectGetPosition(this);
var deltaTime = Math.Max(1.0, elapsedTime.TotalSeconds);
var deltaMouse = currentMousePosition - lastMousePosition;
lastMousePosition = currentMousePosition;
var power = 1.7;
if (rightButtonPressed && !leftButtonPressed)
power = 1.25;
deltaMouse.X = Math.Pow(Math.Abs(deltaMouse.X), power) * Math.Sign(deltaMouse.X) * deltaTime;
deltaMouse.Y = Math.Pow(Math.Abs(deltaMouse.Y), power) * Math.Sign(deltaMouse.Y) * deltaTime;
if (!leftButtonPressed && !rightButtonPressed)
return;
moveThresholdReached = moveThresholdReached || deltaMouse.Length >= 3.0;
if (!moveThresholdReached)
return;
var mainCamera = this.MainCamera as Transformation;
if (rightButtonPressed && leftButtonPressed)
{
mainCamera.LocalPosition += mainCamera.LocalUpDirection * -(float)deltaMouse.Y * 0.005f +
mainCamera.LocalRightDirection * (float)deltaMouse.X * 0.005f;
}
else if (leftButtonPressed)
{
mainCamera.LocalPosition += mainCamera.LocalDirection * -(float)deltaMouse.Y * 0.005f +
mainCamera.LocalRightDirection * (float)deltaMouse.X * 0.005f;
}
else if (rightButtonPressed)
{
var yawPitchRoll = mainCamera.LocalRotationYPR;
yawPitchRoll.X += (float)deltaMouse.X * 0.005f;
yawPitchRoll.Y += (float)deltaMouse.Y * 0.005f;
if (yawPitchRoll.Y > Math.PI / 2)
yawPitchRoll.Y = (float)Math.PI / 2;
else if (yawPitchRoll.Y < -Math.PI / 2)
yawPitchRoll.Y = -(float)Math.PI / 2;
mainCamera.LocalRotationYPR = yawPitchRoll;
}
}
}
public class PickableResourcesDataSource : List
{
public static PickableResourcesDataSource CreateFromResource(Resource rootNode)
{
var pickablesDataResource = new PickableResourcesDataSource();
List> pickables = new List>();
FindAllPickables(rootNode, null, pickables);
foreach (var pickable in pickables)
{
pickablesDataResource.Add(new PickableResourceInfo()
{
PickHull = pickable.Value.PickHull,
Transformation = pickable.Key,
Resource = pickable.Value as IResource
});
}
return pickablesDataResource;
}
private static void FindAllPickables(Resource rootNode, Transformation lastTransformation, List> pickables)
{
if (rootNode.Children != null)
foreach (var child in rootNode.Children)
{
if (child is Transformation)
lastTransformation = child as Transformation;
if (child is IGeometryBuffer && lastTransformation != null)
{
var geoBuffer = child as IGeometryBuffer;
if (geoBuffer.IsPickable)
pickables.Add(new KeyValuePair(lastTransformation, geoBuffer));
}
if (child is Resource)
FindAllPickables(child as Resource, lastTransformation, pickables);
}
}
}
public class PickableResourceInfo
{
public IResource Resource;
public Transformation Transformation;
public IPickHull PickHull;
}
}