PageRenderTime 1273ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/Balder/Display/Viewport.cs

#
C# | 409 lines | 235 code | 68 blank | 106 comment | 22 complexity | 4fff3ab4e7a59fabc2a1d6a4d71a12c6 MD5 | raw file
Possible License(s): Apache-2.0
  1. #region License
  2. //
  3. // Author: Einar Ingebrigtsen <einar@dolittle.com>
  4. // Copyright (c) 2007-2011, DoLittle Studios
  5. //
  6. // Licensed under the Microsoft Permissive License (Ms-PL), Version 1.1 (the "License")
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the license at
  9. //
  10. // http://balder.codeplex.com/license
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. using Balder.Debug;
  20. using Balder.Execution;
  21. using Balder.Input;
  22. using Balder.Math;
  23. using Balder.Objects;
  24. using Balder.Rendering;
  25. using Balder.View;
  26. #if(XAML)
  27. using System.Windows;
  28. using Ninject;
  29. #endif
  30. using Vector = Balder.Math.Vector;
  31. namespace Balder.Display
  32. {
  33. /// <summary>
  34. /// Represents a viewport within a display - the Viewport is a 2D rectangle representing a clipping region.
  35. /// The viewport also holds the view used to render and also holds the scene that contains the objects that
  36. /// will be rendered within the viewport
  37. /// </summary>
  38. #if(XAML)
  39. public class Viewport : FrameworkElement,
  40. #else
  41. public class Viewport
  42. #endif
  43. IHavePropertyContainer
  44. {
  45. private readonly IRuntimeContext _runtimeContext;
  46. public const float MinDepth = 0f;
  47. public const float MaxDepth = 1f;
  48. private Ray _mousePickRay;
  49. #if(XAML)
  50. /// <summary>
  51. /// Creates a viewport
  52. /// </summary>
  53. public Viewport()
  54. : this(Runtime.Instance.Kernel.Get<IRuntimeContext>())
  55. {
  56. }
  57. #endif
  58. /// <summary>
  59. /// Creates a viewport
  60. /// </summary>
  61. /// <param name="runtimeContext">RuntimeContext that the viewport belongs to</param>
  62. public Viewport(IRuntimeContext runtimeContext)
  63. {
  64. _runtimeContext = runtimeContext;
  65. DebugInfo = new DebugInfo();
  66. Statistics = new ViewportStatistics();
  67. _mousePickRay = new Ray(Vector.Zero, Vector.Forward);
  68. runtimeContext.MessengerContext.SubscriptionsFor<PassiveRenderingSignal>().AddListener(this, PassiveRender);
  69. runtimeContext.MessengerContext.SubscriptionsFor<RenderMessage>().AddListener(this, Render);
  70. runtimeContext.MessengerContext.SubscriptionsFor<PrepareMessage>().AddListener(this, Prepare);
  71. }
  72. public void Uninitialize()
  73. {
  74. _runtimeContext.MessengerContext.SubscriptionsFor<PassiveRenderingSignal>().RemoveListener(this, PassiveRender);
  75. _runtimeContext.MessengerContext.SubscriptionsFor<RenderMessage>().RemoveListener(this, Render);
  76. _runtimeContext.MessengerContext.SubscriptionsFor<PrepareMessage>().RemoveListener(this, Prepare);
  77. }
  78. /// <summary>
  79. /// Gets or sets the x position in pixelsof the viewport within the display, where 0 is the left
  80. /// </summary>
  81. public int XPosition { get; set; }
  82. /// <summary>
  83. /// Gets or sets the y position in pixels of the viewport within the display, where 0 is the top.
  84. /// </summary>
  85. public int YPosition { get; set; }
  86. /// <summary>
  87. /// Gets or sets the width in pixels of the viewport within the display
  88. /// </summary>
  89. #if(XAML)
  90. public new int Width { get; set; }
  91. #else
  92. public int Width { get; set; }
  93. #endif
  94. /// <summary>
  95. /// Gets or sets the height in pixels of the viewport within the display
  96. /// </summary>
  97. #if(XAML)
  98. public new int Height { get; set; }
  99. #else
  100. public int Height { get; set; }
  101. #endif
  102. /// <summary>
  103. /// Gets or sets the scene to use during rendering
  104. /// </summary>
  105. public Scene Scene { get; set; }
  106. /// <summary>
  107. /// Gets or sets the view to be used during rendering
  108. /// </summary>
  109. public IView View { get; set; }
  110. /// <summary>
  111. /// Gets or sets the debug info for the Viewport
  112. /// </summary>
  113. public DebugInfo DebugInfo { get; set; }
  114. /// <summary>
  115. /// Get the display in which the viewport is rendered to
  116. /// </summary>
  117. public IDisplay Display { get; internal set; }
  118. /// <summary>
  119. /// Gets or sets the Skybox for the display
  120. /// </summary>
  121. public Skybox Skybox { get; set; }
  122. /// <summary>
  123. /// Get the matrix that represents the matrix for converting to screen coordinates
  124. /// </summary>
  125. public Matrix ScreenMatrix { get; private set; }
  126. /// <summary>
  127. /// Get the matrix representing the combined View * Projection matrices
  128. /// </summary>
  129. public Matrix ViewProjectionMatrix { get; private set; }
  130. /// <summary>
  131. /// Get the matrix representing the combined View * Projection * Screen matrices
  132. /// </summary>
  133. public Matrix ViewProjectionScreenMatrix { get; private set; }
  134. /// <summary>
  135. /// Get the aspect ratio for the viewport
  136. /// </summary>
  137. public float AspectRatio { get { return ((float)Width) / ((float)Height); } }
  138. /// <summary>
  139. /// Get the statistics for the viewport
  140. /// </summary>
  141. public ViewportStatistics Statistics { get; private set; }
  142. /// <summary>
  143. /// Identifies the MousePickRayStart property
  144. /// </summary>
  145. public static readonly Property<Viewport, Coordinate> MousePickRayStartProperty =
  146. Property<Viewport, Coordinate>.Register(v => v.MousePickRayStart);
  147. /// <summary>
  148. /// Gets or sets the ray start for a mouse
  149. /// </summary>
  150. public Coordinate MousePickRayStart
  151. {
  152. get { return MousePickRayStartProperty.GetValue(this); }
  153. set { MousePickRayStartProperty.SetValue(this, value); }
  154. }
  155. /// <summary>
  156. /// Identifies the MousePickRayDirection property
  157. /// </summary>
  158. public static readonly Property<Viewport, Coordinate> MousePickRayDirectionProperty =
  159. Property<Viewport, Coordinate>.Register(v => v.MousePickRayDirection);
  160. /// <summary>
  161. /// Gets or sets the ray direction for a mouse
  162. /// </summary>
  163. public Coordinate MousePickRayDirection
  164. {
  165. get { return MousePickRayDirectionProperty.GetValue(this); }
  166. set { MousePickRayDirectionProperty.SetValue(this, value); }
  167. }
  168. public void HandleMouseDebugInfo(int x, int y, Node hitNode)
  169. {
  170. if (DebugInfo.ShowMouseHitDetectionRay)
  171. {
  172. _mousePickRay = GetPickRay(x, y);
  173. MousePickRayStart = _mousePickRay.Position;
  174. MousePickRayDirection = _mousePickRay.Direction;
  175. }
  176. }
  177. /// <summary>
  178. /// Get a node at a specified position relative to a specific viewport
  179. /// </summary>
  180. /// <param name="x">X position</param>
  181. /// <param name="y">Y position</param>
  182. /// <returns>A RenderableNode - null if it didn't find any node at the position</returns>
  183. public virtual RenderableNode GetNodeAtPosition(int x, int y)
  184. {
  185. var pickRay = GetPickRay(x, y);
  186. RenderableNode closestNode = null;
  187. var closestDistance = float.MaxValue;
  188. foreach (var node in Scene.RenderableNodes)
  189. {
  190. GetNodeAtPosition(node, pickRay, ref closestNode, ref closestDistance);
  191. }
  192. return closestNode;
  193. }
  194. private void GetNodeAtPosition(INode node, Ray pickRay, ref RenderableNode closestNode, ref float closestDistance)
  195. {
  196. if (!node.IsIntersectionTestEnabled || !IsNodeVisible(node))
  197. {
  198. return;
  199. }
  200. if (node is ICanGetNodeAtPosition)
  201. {
  202. closestDistance = GetClosestDistanceFromNode(node, pickRay, closestDistance, ref closestNode);
  203. }
  204. else
  205. {
  206. if (node is RenderableNode)
  207. {
  208. var pickNode = node as RenderableNode;
  209. var distance = pickNode.Intersects(this, pickRay);
  210. if (null != distance && distance.Value < closestDistance)
  211. {
  212. closestDistance = distance.Value;
  213. closestNode = pickNode;
  214. }
  215. }
  216. if (node is IHaveChildren)
  217. {
  218. closestDistance = GetClosestDistanceFromChildren(node, pickRay, closestDistance, ref closestNode);
  219. }
  220. }
  221. }
  222. private float GetClosestDistanceFromChildren(INode node, Ray pickRay, float closestDistance, ref RenderableNode closestNode)
  223. {
  224. var childrenNode = node as IHaveChildren;
  225. foreach (var child in childrenNode.Children)
  226. {
  227. GetNodeAtPosition(child, pickRay, ref closestNode, ref closestDistance);
  228. }
  229. return closestDistance;
  230. }
  231. private float GetClosestDistanceFromNode(INode node, Ray pickRay, float closestDistance, ref RenderableNode closestNode)
  232. {
  233. float? distance = null;
  234. RenderableNode nodeAtPosition = null;
  235. ((ICanGetNodeAtPosition)node).GetNodeAtPosition(this, pickRay, ref nodeAtPosition, ref distance);
  236. if (null != distance && distance.Value < closestDistance)
  237. {
  238. closestNode = nodeAtPosition;
  239. closestDistance = distance.Value;
  240. }
  241. return closestDistance;
  242. }
  243. private bool IsNodeVisible(INode node)
  244. {
  245. if (node is ICanBeVisible)
  246. {
  247. return ((ICanBeVisible)node).IsVisible;
  248. }
  249. return true;
  250. }
  251. public Ray GetPickRay(int x, int y)
  252. {
  253. return View.GetPickRay(x, y);
  254. }
  255. private bool _render;
  256. private void PassiveRender(PassiveRenderingSignal message)
  257. {
  258. _render = true;
  259. }
  260. public void Render(RenderMessage renderMessage)
  261. {
  262. ScreenMatrix = Matrix.CreateScreenTranslation(Width, Height);
  263. ViewProjectionScreenMatrix = View.ViewMatrix * View.ProjectionMatrix * ScreenMatrix;
  264. if (null != View)
  265. {
  266. View.Update(this);
  267. if (null != Skybox &&
  268. Skybox.IsEnabled &&
  269. (_render || !_runtimeContext.PassiveRendering))
  270. {
  271. Skybox.SkyboxContext.Render(this, Skybox);
  272. }
  273. }
  274. Scene.Render(this);
  275. if (DebugInfo.ShowMouseHitDetectionRay)
  276. {
  277. DebugRenderer.Instance.RenderRay(_mousePickRay.Position, _mousePickRay.Direction, this);
  278. }
  279. _runtimeContext.MessengerContext.Send(RenderDoneMessage.Default);
  280. _render = false;
  281. }
  282. /// <summary>
  283. /// Unproject a 2D coordinate into 3D. Basically convert a 2D point with depth
  284. /// information (Z) into a real 3D coordinate.
  285. /// </summary>
  286. /// <param name="source">Point to unproject</param>
  287. /// <param name="projection">Projection matrix</param>
  288. /// <param name="view">View matrix</param>
  289. /// <param name="world">World matrix</param>
  290. /// <returns>Unprojected 3D coordinate</returns>
  291. public Vector Unproject(Vector source, Matrix projection, Matrix view, Matrix world)
  292. {
  293. return View.Unproject(source, projection, view, world);
  294. }
  295. /// <summary>
  296. /// Projects a vector into screen-coordinates
  297. /// </summary>
  298. /// <param name="source">Source vector to project</param>
  299. /// <param name="world">World matrix for the vector</param>
  300. /// <returns>Projected vector</returns>
  301. /// <remarks>
  302. /// Contribution from Dmitriy Kataskin
  303. /// </remarks>
  304. public Vector Project(Vector source, Matrix world)
  305. {
  306. var matrix = (world * View.ViewMatrix) * View.ProjectionMatrix;
  307. return ProjectWithMatrix(source, matrix);
  308. }
  309. public Vector ProjectWithMatrix(Vector source, Matrix matrix)
  310. {
  311. return ProjectWithMatrix(source.X, source.Y, source.Z, matrix);
  312. }
  313. public Vector ProjectWithMatrix(float x, float y, float z, Matrix matrix)
  314. {
  315. var vector = Vector.TransformNormal(x, y, z, matrix);
  316. var a = (((x * matrix.M14) + (y * matrix.M24)) + (z * matrix.M34)) + matrix.M44;
  317. if (!MathHelper.WithinEpsilon(a, 1f))
  318. {
  319. vector = vector / (a);
  320. }
  321. vector.X = (((vector.X + 1f) * 0.5f) * Width);
  322. vector.Y = (((-vector.Y + 1f) * 0.5f) * Height);
  323. vector.Z = (vector.Z * (MaxDepth - MinDepth)) + MinDepth;
  324. return vector;
  325. }
  326. private void Prepare(PrepareMessage prepareMessage)
  327. {
  328. Scene.Prepare(this);
  329. }
  330. IPropertyContainer _propertyContainer;
  331. public IPropertyContainer PropertyContainer
  332. {
  333. get
  334. {
  335. if (_propertyContainer == null)
  336. _propertyContainer = new PropertyContainer(this);
  337. return _propertyContainer;
  338. }
  339. }
  340. }
  341. }