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 / 60.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; } }