/// <summary> /// Create a view matrix for this Camera /// </summary> public static Matrix4 GetViewMatrix(ICamera2 camera, bool isOrtho = true) { Transform2 transform = camera.GetWorldTransform(); Matrix4 m = Matrix4.CreateRotationZ(transform.Rotation); Vector3 lookat = new Vector3(transform.Position) + Vector3.Transform(new Vector3(0, 0, -1), m); Vector3 eye; Matrix4 perspective; if (isOrtho) { float x = camera.ViewOffset.X / 2; float y = camera.ViewOffset.Y / 2; float width = transform.Scale.X * camera.Aspect; float height = transform.Scale.Y; x *= transform.Scale.X * camera.Aspect; y *= transform.Scale.Y; perspective = Matrix4.CreateOrthographicOffCenter(x - width / 2, x + width / 2, y - height / 2, y + height / 2, camera.ZNear, camera.ZFar); eye = new Vector3(transform.Position) + new Vector3(0, 0, 50); return Matrix4.LookAt(eye, lookat, new Vector3(GetUp(camera))) * perspective; } perspective = Matrix4.CreatePerspectiveFieldOfView((float)camera.Fov, camera.Aspect, 0.01f, 10000f); perspective = Matrix4.CreateScale(transform.Scale.X, transform.Scale.Y, Math.Abs(transform.Size)) * perspective; eye = new Vector3(transform.Position) + new Vector3(0, 0, (float)GetWorldZ(camera)); return Matrix4.LookAt(eye, lookat, new Vector3(GetUp(camera))) * perspective * Matrix4.CreateTranslation(new Vector3(-camera.ViewOffset.X, -camera.ViewOffset.Y, 0)); }
//get xy world offset needed to make v appear to overlap target in screen space. public static Vector2 GetOverlapOffset(ICamera2 camera, Vector3 v, Vector3 target) { Vector3 cameraPos = new Vector3(camera.GetWorldTransform().Position); cameraPos.Z = (float)GetWorldZ(camera); float x = (v.X - cameraPos.X) / (v.Z - cameraPos.Z) - (target.X - cameraPos.X) / (target.Z - cameraPos.Z); float y = (v.Y - cameraPos.Y) / (v.Z - cameraPos.Z) - (target.Y - cameraPos.Y) / (target.Z - cameraPos.Z); Vector2 offset = -new Vector2(x, y) * (v.Z - cameraPos.Z); return offset; }
public static Transform2 GetWorldViewpoint(ICamera2 camera) { return new Transform2(camera.ViewOffset).Transform(camera.GetWorldTransform()); }
private static double GetWorldZ(ICamera2 camera) { return Math.Abs(camera.GetWorldTransform().Size / (2 * Math.Tan(camera.Fov / 2))); }
private static Vector2 GetUp(ICamera2 camera) { Matrix4 m = Matrix4.CreateRotationZ(camera.GetWorldTransform().Rotation); return Vector2Ext.Transform(new Vector2(0, 1), m); }
public static PortalView CalculatePortalViews(IList<IPortal> portals, ICamera2 camera, int depth) { Debug.Assert(camera != null); Debug.Assert(depth >= 0); Debug.Assert(portals != null); List<IntPoint> view = ClipperConvert.ToIntPoint(CameraExt.GetWorldVerts(camera)); List<List<IntPoint>> paths = new List<List<IntPoint>>(); paths.Add(view); PortalView portalView = new PortalView(null, CameraExt.GetViewMatrix(camera), view, new LineF[0], new LineF[0]); Vector2 camPos = camera.GetWorldTransform().Position; List<Func<bool>> actionList = new List<Func<bool>>(); foreach (IPortal p in portals) { actionList.Add(() => CalculatePortalViews(p, null, portals, CameraExt.GetViewMatrix(camera), camPos, camPos - camera.GetWorldVelocity().Position / Controller.DrawsPerSecond, portalView, Matrix4.Identity, actionList)); } while (actionList.Count > 0 && depth > 0) { bool result = actionList.First().Invoke(); if (result) { depth--; } actionList.RemoveAt(0); } return portalView; }
/// <summary> /// Draw the edges for each portal potentially including motion blur. /// </summary> /// <param name="portalViewList"></param> /// <param name="cam"></param> private void RenderPortalEdges(List<PortalView> portalViewList, ICamera2 cam) { int iterations = Math.Min(portalViewList.Count, StencilMaxValue); /* Escape early if there aren't any visible portals. * The first iteration is just for the main view which doesn't have portal edges.*/ if (iterations <= 1) { return; } GL.Clear(ClearBufferMask.DepthBufferBit); SetEnable(EnableCap.StencilTest, false); GL.Disable(EnableCap.DepthTest); GL.Clear(ClearBufferMask.StencilBufferBit); Clipper c = new Clipper(); c.StrictlySimple = true; Model portalEdges = new Model(); portalEdges.SetTexture(Textures["lineBlur.png"]); SetEnable(EnableCap.Blend, true); for (int i = 1; i < iterations; i++) { for (int j = 0; j < 2; j++) { LineF line = portalViewList[i].FovLines[j]; float minWidth = Math.Abs(cam.GetWorldTransform().Size) / 300; double angleDiff = GetLineBlurAngle(line, portalViewList[i].FovLinesPrevious[j]); float widthEnd = (float)Math.Tan(angleDiff) * line.Length; widthEnd = Math.Max(widthEnd, minWidth); Vector2[] lineWidth = PolygonFactory.CreateLineWidth(line, minWidth); Vector2 camPos = cam.GetWorldTransform().Position; Vector2[] lineWidthOff = Vector2Ext.Transform(lineWidth, Matrix4.CreateTranslation(new Vector3(-camPos))); Vector2[] lineTarget = PolygonFactory.CreateLineWidth(line.Translate(-camPos), minWidth, widthEnd); Matrix4d homography = Matrix4d.CreateTranslation(new Vector3d((Vector2d)(-camPos))); homography *= MathExt.GetHomography(lineWidthOff, lineTarget); homography *= Matrix4d.CreateTranslation(new Vector3d((Vector2d)camPos)); bool obscured = true; for (int k = 0; k < portalViewList[i].Parent.Paths.Count; k++) { List<IntPoint> path = portalViewList[i].Parent.Paths[k]; if (Clipper.PointInPolygon(ClipperConvert.ToIntPoint(line[0]), path) == 1) { obscured = false; break; } } if (obscured) { continue; } foreach (PortalView p in portalViewList[i].Parent.Children) { if (p == portalViewList[i]) { continue; } if (p.PortalLine.IsInsideFOV(camPos, line[0])) { obscured = true; break; } } if (obscured) { continue; } int index = ModelFactory.AddPolygon((Mesh)portalEdges.Mesh, lineWidth); IMesh mesh = portalEdges.Mesh; for (int k = index; k < mesh.GetVertices().Count; k++) { Vertex vertex = mesh.GetVertices()[k]; Vector3 pos = Vector3Ext.Transform(vertex.Position, homography); pos.Z = CameraExt.UnitZToWorld(cam, pos.Z); Vector2 texCoord; Vector2 v = new Vector2(vertex.Position.X, vertex.Position.Y); double distance = MathExt.PointLineDistance(v, line.GetPerpendicularLeft(), false); double texCoordX = MathExt.PointLineDistance(v, line, false) / minWidth; if (line.GetSideOf(v) == Side.Left) { texCoordX *= -1; } texCoordX += 0.5; texCoord = new Vector2((float)texCoordX, (float)(distance / line.Length)); mesh.GetVertices()[k] = new Vertex(pos, texCoord); } } } RenderModel(portalEdges, CameraExt.GetViewMatrix(cam, false)); SetEnable(EnableCap.Blend, false); GL.Enable(EnableCap.DepthTest); }