From c84d87402ad41794953b47a76e5a3704d7639760 Mon Sep 17 00:00:00 2001 From: Daniel Walder Date: Tue, 5 Feb 2013 19:32:49 +1000 Subject: [PATCH] No idea how these files got corrupt, fixing them. --- .../SolidVertexArray.cs | 86 ++++---- Sledge.DataStructures/MapObjects/Entity.cs | 6 +- Sledge.DataStructures/MapObjects/Map.cs | 10 +- Sledge.Editor/Documents/Document.cs | 9 +- Sledge.Editor/Sledge.Editor.csproj | 23 +- Sledge.Providers/Texture/TexturePackage.cs | 169 +++++++------- Sledge.UI/Viewport2D.cs | 4 +- Sledge.UI/Viewport3D.cs | 208 +++++++++--------- 8 files changed, 262 insertions(+), 253 deletions(-) diff --git a/Sledge.DataStructures.Rendering/SolidVertexArray.cs b/Sledge.DataStructures.Rendering/SolidVertexArray.cs index ee767f3a9..8da5a9d19 100644 --- a/Sledge.DataStructures.Rendering/SolidVertexArray.cs +++ b/Sledge.DataStructures.Rendering/SolidVertexArray.cs @@ -8,8 +8,8 @@ namespace Sledge.DataStructures.Rendering { /// - /// A solid vertex array collects and stores a VBO for a single soll solids in the map. - /// Faces are grouped by texture and then split into optimised rendering later on. + /// A solid vertex array collects and stores a VBO for all solids in the map. + /// Faces are grouped by texture and then split into subsets for optimised rendering later on. /// public class SolidVertexArray { @@ -33,7 +33,8 @@ static SolidVertexArray() public List> TextureSubsets { get; private set; } public List> WireframeSubsets { get; private set; } public Dictionary FaceOffsets { get; private set; } - private readonly Dictionary Entity private readonly Dictionary> _arrays; + public Dictionary EntityOffsets { get; private set; } + private readonly Dictionary> _arrays; public void Bind(object context, int index) { @@ -64,10 +65,10 @@ public SolidVertexArray(IEnumerable objects) TextureSubsets = new List>(); WireframeSubsets = new List>(); FaceOffsets = new Dictionary(); - GetArrayData(objects, out count, out array, out indices, out wirefrEntityOffsets = new DictionaryeIndices, TextureSubsets, WireframeSubsets, FaceOffsets); + EntityOffsets = new Dictionary(); + GetArrayData(objects, out count, out array, out indices, out wireframeIndices, TextureSubsets, WireframeSubsets, FaceOffsets, EntityOffsets); - Array.Update(count, array, new[] {indices, wireframeIndices}); - , Entityn, Modes, count, sizeof(float), array, new[] { indices, wireframeIndices}); + Array = new VertexBuffer(Specification, Modes, count, sizeof(float), array, new[] { indices, wireframeIndices}); } /// @@ -84,19 +85,21 @@ public void Update(IEnumerable objects) TextureSubsets.Clear(); WireframeSubsets.Clear(); FaceOffsets.Clear(); - GetArrayData(objects, out count, out array, out indices, out wireframeIndices, TextureEntity out wireframeIndices, TextureSubsets, WireframeSubsets, FaceOffsets); + EntityOffsets.Clear(); + GetArrayData(objects, out count, out array, out indices, out wireframeIndices, TextureSubsets, WireframeSubsets, FaceOffsets, EntityOffsets); Array.Update(count, array, new[] {indices, wireframeIndices}); - , Entity } + } public void UpdatePartial(IEnumerable objects) { UpdatePartial(objects.OfType().SelectMany(x => x.Faces)); + UpdatePartial(objects.OfType().Where(x => x.Children.Count == 0)); } public void UpdatePartial(IEnumerable faces) { - var list = UpdatePartial(objects.OfType().Where(x => x.Children.Count == 0 var list = new float[128]; // 128 is large enough for most faces (up to 11 faces) + var list = new float[128]; // 128 is large enough for most faces (up to 11 faces) foreach (var face in faces) { if (!FaceOffsets.ContainsKey(face)) continue; @@ -108,9 +111,7 @@ public void UpdatePartial(IEnumerable faces) } } - /// - /// Does a loop around the map objects and calculates array data and the subsets - public void UpdatePartial(IEnumerable entities) + public void UpdatePartial(IEnumerable entities) { var list = new float[6 * 4 * SpecSize]; foreach (var entity in entities) @@ -122,7 +123,12 @@ public void UpdatePartial(IEnumerable entities) { idx = WriteFace(list, idx, face); } - Array.UpdatePartial(offset, list.Length calculates array data and the subsets + Array.UpdatePartial(offset, list.Length, list); + } + } + + /// + /// Does a loop around the map objects and calculates array data and the subsets /// /// The objects in the array /// Outputs the number of verts in the array @@ -132,19 +138,21 @@ public void UpdatePartial(IEnumerable entities) /// The collection of textured subsets to populate /// The collection of wireframe subsets to populate /// - private static void GetArrayData(IEnumerable objects, out int count, out float[] array, out uint[] indices, ou/// + private static void GetArrayData(IEnumerable objects, out int count, out float[] array, out uint[] indices, out uint[] wireframeIndices, ICollection> subsets, ICollection> wireframeSubsets, Dictionary faceOffsets, Dictionary entityOffsets) { var obj = objects.Where(x => !x.IsVisgroupHidden && !x.IsCodeHidden).ToList(); var faces = obj.OfType().SelectMany(x => x.Faces).ToList(); - var entities = obj.OfType().Where(x => x.Children.Count == 0 uint index = 0; + var entities = obj.OfType().Where(x => x.Children.Count == 0).ToList(); + var indexList = new List(); + var wireframeIndexList = new List(); + uint index = 0; var idx = 0; - array = new float[SpecSize * faces.Sum(x => x.Vertices.Count)]; + var numVerts = faces.Sum(x => x.Vertices.Count) + entities.Count * 6 * 4; // Entity is always a rec. prism (6 sides, quads) + array = new float[SpecSize * numVerts]; var subsetStart = 0; - var wireframeSubsetvar numVerts = faces.Sum(x => x.Vertices.Count) + entities.Count * 6 * 4; // Entity is always a rec. prism (6 sides, quads) - array = new float[SpecSize * numVerts=> new { x.Texture.Texture })) + var wireframeSubsetStart = 0; + foreach (var group in faces.GroupBy(x => new { x.Texture.Texture })) { foreach (var face in group) { @@ -171,12 +179,7 @@ private static void GetArrayData(IEnumerable objects, out int count, wireframeSubsets.Add(new VertexArraySubset(null, wireframeSubsetStart, wireframeIndexList.Count - wireframeSubsetStart)); wireframeSubsetStart = wireframeIndexList.Count; } - indices = indexList.ToArray(); - wireframeIndices = wireframeIndexList.ToArray(); - count = indices.Length; - } - - private staforeach (var entity in entities) + foreach (var entity in entities) { entityOffsets.Add(entity, idx); foreach (var face in entity.GetFaces()) @@ -189,22 +192,28 @@ private staforeach (var entity in entities) indexList.Add(index); indexList.Add(index + i); indexList.Add(index + i + 1); - }i = (uint) ((i + 1) % face.Vertices.Count); + } + } + for (uint i = 0; i < face.Vertices.Count; i++) + { + var ni = (uint)((i + 1) % face.Vertices.Count); wireframeIndexList.Add(index + i); - wireframeIndexList.Add(index+ ni); + wireframeIndexList.Add(index + ni); } - index += (uint) face.Vertices.Count; - } - - subsets.Add(new VertexArraySubset(group.Key.Texture, subsetStart, indexList.Count face.Vertices.Count; + index += (uint)face.Vertices.Count; } } if (entities.Any()) { - subsets.Add(new VertexArraySubset(null, subsetStart, indexList.Count - subsetStart)); wireframeSubsetStart = wireframeIndexList.Count; + subsets.Add(new VertexArraySubset(null, subsetStart, indexList.Count - subsetStart)); + wireframeSubsets.Add(new VertexArraySubset(null, wireframeSubsetStart, wireframeIndexList.Count - wireframeSubsetStart)); } indices = indexList.ToArray(); - wireframeIndices = wireframeIndexL private static int WriteFace(float[] array, int idx, Face face) + wireframeIndices = wireframeIndexList.ToArray(); + count = indices.Length; + } + + private static int WriteFace(float[] array, int idx, Face face) { float nx = (float) face.Plane.Normal.DX, ny = (float) face.Plane.Normal.DY, @@ -225,12 +234,7 @@ private staforeach (var entity in entities) array[idx++] = (r); array[idx++] = (g); array[idx++] = (b); - array[idx++] = (face.IsSelected || face.Parent.IsSelected ? 1 : 0); - } - return idx; - } - } -}(face.Parent != null && face.Parent.IsSelected) ? 1 : 0); + array[idx++] = (face.IsSelected || (face.Parent != null && face.Parent.IsSelected) ? 1 : 0); } return idx; } diff --git a/Sledge.DataStructures/MapObjects/Entity.cs b/Sledge.DataStructures/MapObjects/Entity.cs index eeb02e8d4..f55e979f5 100644 --- a/Sledge.DataStructures/MapObjects/Entity.cs +++ b/Sledge.DataStructures/MapObjects/Entity.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using Sledge.DatCommonusing Sledge.DataStructures.GameData; +using Sledge.Common; +using Sledge.DataStructures.GameData; using Sledge.DataStructures.Geometric; using Sledge.DataStructures.Transformations; @@ -12,8 +13,7 @@ public class Entity : MapObject public GameDataObject GameData { get; set; } public EntityData EntityData { get; set; } public Coordinate Origin { get; set; } - - publi public ITexture Sprite} + public ITexture Sprite { get; set; } public Entity(long id) : base(id) { diff --git a/Sledge.DataStructures/MapObjects/Map.cs b/Sledge.DataStructures/MapObjects/Map.cs index d6f39b840..0f2d69172 100644 --- a/Sledge.DataStructures/MapObjects/Map.cs +++ b/Sledge.DataStructures/MapObjects/Map.cs @@ -58,9 +58,10 @@ public void PostLoadProcess(GameData.GameData gameData, Func t IDGenerator.Reset(maxObjectId, maxFaceId); // todo visgroups + // WorldSpawn.ForEach(x => x.IsVisgroupHidden, x => x.IsVisgroupHidden = true, true); // Purge empty groups - foreach (var // WorldSpawn.ForEach(x => x.IsVisgroupHidden, x => x.IsVisgroupHidden = true, true);r emptyGroup in WorldSpawn.Find(x => x is Group && !x.Children.Any())) + foreach (var emptyGroup in WorldSpawn.Find(x => x is Group && !x.Children.Any())) { emptyGroup.Parent.Children.Remove(emptyGroup); } @@ -78,12 +79,15 @@ public void PartialPostLoadProcess(Predicate matcher, GameData.GameDa { if (obj is Entity) { - ((Entity)obj).GameData = gameData.Classes.FirstOrDefault(x => x.Name == ((Entityvar gd = gameData.Classes.FirstOrDefault(x => x.Name == ((Entity) obj).EntityData.Name); + var gd = gameData.Classes.FirstOrDefault(x => x.Name == ((Entity) obj).EntityData.Name); var t = gd != null && gd.Behaviours.Any(x => x.Name == "iconsprite" && x.Values.Count == 1) ? textureAccessor(gd.Behaviours.First(x => x.Name == "iconsprite").Values[0]) : null; ((Entity) obj).GameData = gd; - ((Entity) obj).Sprite = t else if (obj is Solid) + ((Entity) obj).Sprite = t; + obj.UpdateBoundingBox(); + } + else if (obj is Solid) { ((Solid)obj).Faces.ForEach(f => { diff --git a/Sledge.Editor/Documents/Document.cs b/Sledge.Editor/Documents/Document.cs index 04c6cd79d..a78f90a59 100644 --- a/Sledge.Editor/Documents/Document.cs +++ b/Sledge.Editor/Documents/Document.cs @@ -12,13 +12,14 @@ using Sledge.Editor.Tools; using Sledge.Editor.UI; using Sledge.Editor.Visgroups; +using Sledge.Graphics; using Sledge.Graphics.Helpers; -usine.Graphics.Helpers; using Sledge.Providers; using Sledge.Providers.GameData; using Sledge.Providers.Texture; using Sledge.Settings; -using Path = System.IO.PaSledge.UItem.IO.Path; +using Sledge.UI; +using Path = System.IO.Path; namespace Sledge.Editor.Documents { @@ -33,7 +34,9 @@ public class Document public bool HideFaceMask { get; set; } - private RenderManager Renderer { geublic RenderManager Renderer { get; privateanager Selection { get; private set; } + public RenderManager Renderer { get; private set; } + + public SelectionManager Selection { get; private set; } public HistoryManager History { get; private set; } private readonly DocumentSubscriptions _subscriptions; diff --git a/Sledge.Editor/Sledge.Editor.csproj b/Sledge.Editor/Sledge.Editor.csproj index 4aeaf6bbf..78008ea1e 100644 --- a/Sledge.Editor/Sledge.Editor.csproj +++ b/Sledge.Editor/Sledge.Editor.csproj @@ -107,14 +107,14 @@ - - - + - + + + @@ -143,11 +143,11 @@ Code + Code - RenderManagerRenderableansformedDisplayListRenderable.cs" /> @@ -326,6 +326,12 @@ Settings.settings True + + PreserveNewest + + + PreserveNewest + @@ -333,12 +339,7 @@ Sledge.Common - {FC8A5448-0F8 - PreserveNewest - - - PreserveNewest - + {FC8A5448-0F8B-4828-AC49-8D6138E20254} Sledge.Database diff --git a/Sledge.Providers/Texture/TexturePackage.cs b/Sledge.Providers/Texture/TexturePackage.cs index f9d36ec88..d0b80af56 100644 --- a/Sledge.Providers/Texture/TexturePackage.cs +++ b/Sledge.Providers/Texture/TexturePackage.cs @@ -1,88 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using Sledge.Graphics; -using Sledge.Graphics.Helpers; - -namespace Sledge.Providers.Texture -{ - public class TexturePackage - { - private static readonly Dictionary LoadedPackages = new Dictionary(); - private static readonly Dictionary LoadedItems = new Dictionary(); - - public static void ClearLoadedPackages() - { - LoadedPackages.Clear(); - LoadedItems.Clear(); - TextureHelper.ClearLoadedTextures(); - } - - public static void LoadTextureData(string name) - { - if (TextureHelper.Exists(name)) return; - var item = LoadedItems[name]; - if (item != null) - { - TextureProvider.LoadTextureFromPackage(item.Package, item.Name); - } - } - - public static void LoadTextureData(IEnumerable names) - { - var texes = names.Where(x => !TextureHelper.Exists(x)); - TextureProvider.LoadTexturesFromPackages(LoadedPackages.Values, texes); - } - - public static IEnumerable GetLoadedPackages() - { - return LoadedPackages.Values; - } - - public static IEnumerable GetLoadedItems() - { - return LoadedItems.Values; - } - - public static TextureItem GetItem(string name) - { - return LoadedItems.ContainsKey(name) ? LoadedItems[name] : null; - } - +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Sledge.Graphics; +using Sledge.Graphics.Helpers; + +namespace Sledge.Providers.Texture +{ + public class TexturePackage + { + private static readonly Dictionary LoadedPackages = new Dictionary(); + private static readonly Dictionary LoadedItems = new Dictionary(); + + public static void ClearLoadedPackages() + { + LoadedPackages.Clear(); + LoadedItems.Clear(); + TextureHelper.ClearLoadedTextures(); + } + + public static void LoadTextureData(string name) + { + if (TextureHelper.Exists(name)) return; + var item = LoadedItems[name]; + if (item != null) + { + TextureProvider.LoadTextureFromPackage(item.Package, item.Name); + } + } + + public static void LoadTextureData(IEnumerable names) + { + var texes = names.Where(x => !TextureHelper.Exists(x)); + TextureProvider.LoadTexturesFromPackages(LoadedPackages.Values, texes); + } + + public static IEnumerable GetLoadedPackages() + { + return LoadedPackages.Values; + } + + public static IEnumerable GetLoadedItems() + { + return LoadedItems.Values; + } + + public static TextureItem GetItem(string name) + { + return LoadedItems.ContainsKey(name) ? LoadedItems[name] : null; + } + public static void Load(string file) { file = file.ToLower(); - - if (!(File.Exists(file) || Directory.Exists(file)) || LoadedPackages.ContainsKey(file)) return; - var tp = new TexturePackage(file); - tp.LoadAllTextureItems(); - LoadedPackages.Add(file, tp); - } - - public string PackageFile { get; private set; } - public Dictionary Items { get; private set; } - - private TexturePackage(string packageFile) - { - PackageFile = packageFile; - Items = new Dictionary(); - } - - /// - /// Tells this package to retrieve all the texture names from the file. /// This will not get the actual texture data, but it will get some metadata - - /// like name, width, height, and so on. - /// - public void LoadAllTextureItems() - { - foreach (var ti in TextureProvider.GetAllTextureItemsFromPackage(this)) - { - if (LoadedItems.ContainsKey(ti.Name)) continue; - Items.Add(ti.Name, ti); - LoadedItems.Add(ti.Name, ti); - } - } - } -} + if (!(File.Exists(file) || Directory.Exists(file)) || LoadedPackages.ContainsKey(file)) return; + var tp = new TexturePackage(file); + tp.LoadAllTextureItems(); + LoadedPackages.Add(file, tp); + } + + public string PackageFile { get; private set; } + public Dictionary Items { get; private set; } + + private TexturePackage(string packageFile) + { + PackageFile = packageFile; + Items = new Dictionary(); + } + + /// + /// Tells this package to retrieve all the texture names from the file. + /// This will not get the actual texture data, but it will get some metadata + /// like name, width, height, and so on. + /// + public void LoadAllTextureItems() + { + foreach (var ti in TextureProvider.GetAllTextureItemsFromPackage(this)) + { + if (LoadedItems.ContainsKey(ti.Name)) continue; + Items.Add(ti.Name, ti); + LoadedItems.Add(ti.Name, ti); + } + } + } +} diff --git a/Sledge.UI/Viewport2D.cs b/Sledge.UI/Viewport2D.cs index 7a90598aa..ad2aa6404 100644 --- a/Sledge.UI/Viewport2D.cs +++ b/Sledge.UI/Viewport2D.cs @@ -178,13 +178,13 @@ public Coordinate ZeroUnusedCoordinate(Coordinate c) public override void SetViewport() { base.SetViewport(); - //Viewport.Orthographic(0, 0, Width, Height, -50000, 50000); + Viewport.Orthographic(0, 0, Width, Height, -50000, 50000); } public override Matrix4 GetViewportMatrix() { const float near = -1000000; - const float far = 1000000; + const float far = 1000000; return Matrix4.CreateOrthographic(Width, Height, near, far); } diff --git a/Sledge.UI/Viewport3D.cs b/Sledge.UI/Viewport3D.cs index 5691a654c..8fa9dfec8 100644 --- a/Sledge.UI/Viewport3D.cs +++ b/Sledge.UI/Viewport3D.cs @@ -1,23 +1,23 @@ -using OpenTK.Graphics.OpenGL; -using Sledge.Graphics; -using Sledge.DataStructures.Geometric; -using OpenTK; -using Sledge.Graphics.Helpers; -using Matrix = Sledge.Graphics.Helpers.Matrix; - -namespace Sledge.UI -{ - public class Viewport3D : ViewportBase - { - public Camera Camera { get; set; } - - public Viewport3D() - { - Camera = new Camera(); - } - - public Viewport3D(RenderContext context) : base(context) - { +using OpenTK.Graphics.OpenGL; +using Sledge.Graphics; +using Sledge.DataStructures.Geometric; +using OpenTK; +using Sledge.Graphics.Helpers; +using Matrix = Sledge.Graphics.Helpers.Matrix; + +namespace Sledge.UI +{ + public class Viewport3D : ViewportBase + { + public Camera Camera { get; set; } + + public Viewport3D() + { + Camera = new Camera(); + } + + public Viewport3D(RenderContext context) : base(context) + { Camera = new Camera(); } @@ -40,88 +40,86 @@ public override Matrix4 GetCameraMatrix() return cameraMatrix; } - public ove - - public override void SetViewport() - { - base.SetViewport(); - Viewport.Perspective(0, 0, Width, Height); - } - - protected override void UpdateBeforeClearViewport() - { - Camera.Position(); - base.UpdateBeforeClearViewport(); - } - - protected override void UpdateAfterRender() - { - base.UpdateAfterRender(); - Listeners.ForEach(x => x.Render3D()); - - Matrix.Set(MatrixMode.Modelview); - Matrix.Identity(); - Viewport.Orthographic(0, 0, Width, Height); - Listeners.ForEach(x => x.Render2D()); - } - - /// - /// Convert a screen space coordinate into a world space coordinate. - /// The resulting coordinate will be quite a long way from the camera. - /// - /// The screen coordinate (with Y in OpenGL space) - /// The world coordinate - public Coordinate ScreenToWorld(Coordinate screen) - { - screen = new Coordinate(screen.X, screen.Y, 1); - var viewport = new[] { 0, 0, Width, Height }; = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); - var vm - var vm = Matrix4d.LookAt( Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), - new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), - Vec - Vector3d.UnitZ); - return MathFunctions.Unproject(screen, viewport, pm, vm); - } - - /// - /// Convert a world space coordinate into a screen space coordinate. - /// - /// The world coordinate - /// The screen coordinate - public Coordinate WorldToScreen(Coordinate world) - { - var viewport = new[] { 0, 0, Width, Height }; = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); - var vm - var vm = Matrix4d.LookAt( Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), - new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), - Vec - Vector3d.UnitZ); - return MathFunctions.Project(world, viewport, pm, vm); - } - - /// - /// Project the 2D coordinates from the screen coordinates outwards - /// from the camera along the lookat vector, taking the frustrum - /// into account. The resulting line will be run from the camera - /// position along the view axis and end at the back clipping pane. - /// - /// The X coordinate on screen - /// The Y coordinate on screen - /// A line beginning at the camera location and tracing - /// along the 3D projection for at least 1,000,000 units. - public Line CastRayFromScreen(int x, int y) - { - var near = new Coordinate(x, Height - y, 0); - var far = new Coordinate(x, Height - y, 1); = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); - var vm - var vm = Matrix4d.LookAt( Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), - new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), - Vec - Vector3d.UnitZ); - var viewport = new[] { 0, 0, Width, Height }; - var un = MathFunctions.Unproject(near, viewport, pm, vm); - var uf = MathFunctions.Unproject(far, viewport, pm, vm); - return (un == null || uf == null) ? null : new Line(un, uf); - } - } -} + public override void SetViewport() + { + base.SetViewport(); + Viewport.Perspective(0, 0, Width, Height); + } + + protected override void UpdateBeforeClearViewport() + { + Camera.Position(); + base.UpdateBeforeClearViewport(); + } + + protected override void UpdateAfterRender() + { + base.UpdateAfterRender(); + Listeners.ForEach(x => x.Render3D()); + + Matrix.Set(MatrixMode.Modelview); + Matrix.Identity(); + Viewport.Orthographic(0, 0, Width, Height); + Listeners.ForEach(x => x.Render2D()); + } + + /// + /// Convert a screen space coordinate into a world space coordinate. + /// The resulting coordinate will be quite a long way from the camera. + /// + /// The screen coordinate (with Y in OpenGL space) + /// The world coordinate + public Coordinate ScreenToWorld(Coordinate screen) + { + screen = new Coordinate(screen.X, screen.Y, 1); + var viewport = new[] { 0, 0, Width, Height }; + var pm = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); + var vm = Matrix4d.LookAt( + new Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), + new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), + Vector3d.UnitZ); + return MathFunctions.Unproject(screen, viewport, pm, vm); + } + + /// + /// Convert a world space coordinate into a screen space coordinate. + /// + /// The world coordinate + /// The screen coordinate + public Coordinate WorldToScreen(Coordinate world) + { + var viewport = new[] { 0, 0, Width, Height }; + var pm = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); + var vm = Matrix4d.LookAt( + new Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), + new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), + Vector3d.UnitZ); + return MathFunctions.Project(world, viewport, pm, vm); + } + + /// + /// Project the 2D coordinates from the screen coordinates outwards + /// from the camera along the lookat vector, taking the frustrum + /// into account. The resulting line will be run from the camera + /// position along the view axis and end at the back clipping pane. + /// + /// The X coordinate on screen + /// The Y coordinate on screen + /// A line beginning at the camera location and tracing + /// along the 3D projection for at least 1,000,000 units. + public Line CastRayFromScreen(int x, int y) + { + var near = new Coordinate(x, Height - y, 0); + var far = new Coordinate(x, Height - y, 1); + var pm = Matrix4d.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(60), Width / (float)Height, 0.1f, 50000); + var vm = Matrix4d.LookAt( + new Vector3d(Camera.Location.X, Camera.Location.Y, Camera.Location.Z), + new Vector3d(Camera.LookAt.X, Camera.LookAt.Y, Camera.LookAt.Z), + Vector3d.UnitZ); + var viewport = new[] { 0, 0, Width, Height }; + var un = MathFunctions.Unproject(near, viewport, pm, vm); + var uf = MathFunctions.Unproject(far, viewport, pm, vm); + return (un == null || uf == null) ? null : new Line(un, uf); + } + } +}