diff --git a/FFXIVClientStructs b/FFXIVClientStructs index 5e40c29..1905387 160000 --- a/FFXIVClientStructs +++ b/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 5e40c290bea562a52ef5332fdf12076d55921646 +Subproject commit 19053878c4b3525a409737782384ce68442388a5 diff --git a/vnavmesh/Debug/DebugDrawer.cs b/vnavmesh/Debug/DebugDrawer.cs index 518cb71..8313194 100644 --- a/vnavmesh/Debug/DebugDrawer.cs +++ b/vnavmesh/Debug/DebugDrawer.cs @@ -16,18 +16,14 @@ public unsafe class DebugDrawer : IDisposable public RenderTarget? RenderTarget { get; private set; } public EffectMesh? EffectMesh { get; init; } - public SharpDX.Matrix ViewProj { get; private set; } - public SharpDX.Matrix Proj { get; private set; } - public SharpDX.Matrix View { get; private set; } - public SharpDX.Matrix CameraWorld { get; private set; } - public float CameraAzimuth { get; private set; } // facing north = 0, facing west = pi/4, facing south = +-pi/2, facing east = -pi/4 - public float CameraAltitude { get; private set; } // facing horizontally = 0, facing down = pi/4, facing up = -pi/4 - public SharpDX.Vector2 ViewportSize { get; private set; } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate nint GetEngineCoreSingletonDelegate(); - - private nint _engineCoreSingleton; + public Vector3 Origin; + public Matrix4x4 View; + public Matrix4x4 Proj; + public Matrix4x4 ViewProj; + public Vector4 NearPlane; + public float CameraAzimuth; // facing north = 0, facing west = pi/4, facing south = +-pi/2, facing east = -pi/4 + public float CameraAltitude; // facing horizontally = 0, facing down = pi/4, facing up = -pi/4 + public Vector2 ViewportSize; private List<(Vector2 from, Vector2 to, uint col, int thickness)> _viewportLines = new(); private List<(Vector2 center, float radius, uint color)> _viewportCircles = new(); @@ -42,7 +38,6 @@ public DebugDrawer() { Service.Log.Error($"Failed to set up renderer; some debug visualization will be unavailable: {ex}"); } - _engineCoreSingleton = Marshal.GetDelegateForFunctionPointer(Service.SigScanner.ScanText("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 48 89 4C 24 ?? 4C 8D 4D ?? 4C 8D 44 24 ??"))(); } public void Dispose() @@ -54,15 +49,30 @@ public void Dispose() public void StartFrame() { - ViewProj = ReadMatrix(_engineCoreSingleton + 0x1B4); - Proj = ReadMatrix(_engineCoreSingleton + 0x174); - View = ViewProj * SharpDX.Matrix.Invert(Proj); - CameraWorld = SharpDX.Matrix.Invert(View); - CameraAzimuth = MathF.Atan2(View.Column3.X, View.Column3.Z); - CameraAltitude = MathF.Asin(View.Column3.Y); - ViewportSize = ReadVec2(_engineCoreSingleton + 0x1F4); + var controlCamera = FFXIVClientStructs.FFXIV.Client.Game.Control.CameraManager.Instance()->GetActiveCamera(); + var renderCamera = controlCamera != null ? controlCamera->SceneCamera.RenderCamera : null; + if (renderCamera != null) + { + Origin = renderCamera->Origin; + View = renderCamera->ViewMatrix; + View.M44 = 1; // for whatever reason, game doesn't initialize it... + Proj = renderCamera->ProjectionMatrix; + ViewProj = View * Proj; + + // note that game uses reverse-z by default, so we can't just get full plane equation by reading column 3 of vp matrix + // so just calculate it manually: column 3 of view matrix is plane equation for a plane equation going through origin + // proof: + // plane equation p is such that p.dot(Q, 1) = 0 if Q lines on the plane => pw = -Q.dot(n); for view matrix, V43 is -origin.dot(forward) + // plane equation for near plane has Q.dot(n) = O.dot(n) - near => pw = V43 + near + NearPlane = new(View.M13, View.M23, View.M33, View.M43 + renderCamera->NearPlane); + + CameraAzimuth = MathF.Atan2(View.M13, View.M33); + CameraAltitude = MathF.Asin(View.M23); + var device = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device.Instance(); + ViewportSize = new(device->Width, device->Height); + } - EffectMesh?.UpdateConstants(RenderContext, new() { ViewProj = ViewProj, CameraPos = new(CameraWorld.M41, CameraWorld.M42, CameraWorld.M43), LightingWorldYThreshold = 55.Degrees().Cos() }); + EffectMesh?.UpdateConstants(RenderContext, new() { ViewProj = ViewProj, CameraPos = Origin, LightingWorldYThreshold = 55.Degrees().Cos() }); if (RenderTarget == null || RenderTarget.Size != ViewportSize) { @@ -101,10 +111,8 @@ public void EndFrame() public void DrawWorldLine(Vector3 start, Vector3 end, uint color, int thickness = 1) { - var p1 = start.ToSharpDX(); - var p2 = end.ToSharpDX(); - if (ClipLineToNearPlane(ref p1, ref p2)) - _viewportLines.Add((WorldToScreen(p1), WorldToScreen(p2), color, thickness)); + if (ClipLineToNearPlane(ref start, ref end)) + _viewportLines.Add((WorldToScreen(start), WorldToScreen(end), color, thickness)); } public void DrawWorldPolygon(IEnumerable points, uint color, int thickness = 1) @@ -170,37 +178,30 @@ public void DrawWorldTriangle(Vector3 v1, Vector3 v2, Vector3 v3, uint color, in public void DrawWorldPoint(Vector3 p, float radius, uint color, int thickness = 1) { - var pw = p.ToSharpDX(); - var nearPlane = ViewProj.Column3; - if (SharpDX.Vector4.Dot(new(pw, 1), nearPlane) <= 0) + if (Vector4.Dot(new(p, 1), NearPlane) >= 0) return; - var ps = WorldToScreen(pw); + var ps = WorldToScreen(p); foreach (var (from, to) in AdjacentPairs(CurveApprox.Circle(ps, radius, 1))) _viewportLines.Add((from, to, color, thickness)); } public void DrawWorldPointFilled(Vector3 p, float radius, uint color) { - var pw = p.ToSharpDX(); - var nearPlane = ViewProj.Column3; - if (SharpDX.Vector4.Dot(new(pw, 1), nearPlane) <= 0) + if (Vector4.Dot(new(p, 1), NearPlane) >= 0) return; - _viewportCircles.Add((WorldToScreen(pw), radius, color)); + _viewportCircles.Add((WorldToScreen(p), radius, color)); } // arrow with pointer at p coming from the direction of q public void DrawWorldArrowPoint(Vector3 p, Vector3 q, float l, uint color, int thickness = 1) { - var pw = p.ToSharpDX(); - var nearPlane = ViewProj.Column3; - if (SharpDX.Vector4.Dot(new(pw, 1), nearPlane) <= 0) + if (Vector4.Dot(new(p, 1), NearPlane) >= 0) return; - var qw = q.ToSharpDX(); - ClipLineToNearPlane(ref pw, ref qw); - var ps = WorldToScreen(pw); - var qs = WorldToScreen(qw); + ClipLineToNearPlane(ref p, ref q); + var ps = WorldToScreen(p); + var qs = WorldToScreen(q); var d = Vector2.Normalize(qs - ps) * l; var n = new Vector2(-d.Y, d.X) * 0.5f; _viewportLines.Add((ps, ps + d + n, color, thickness)); @@ -254,31 +255,32 @@ private unsafe SharpDX.Vector2 ReadVec2(nint address) return new(p[0], p[1]); } - private bool ClipLineToNearPlane(ref SharpDX.Vector3 a, ref SharpDX.Vector3 b) + private bool ClipLineToNearPlane(ref Vector3 a, ref Vector3 b) { - var n = ViewProj.Column3; // near plane - var an = SharpDX.Vector4.Dot(new(a, 1), n); - var bn = SharpDX.Vector4.Dot(new(b, 1), n); - if (an <= 0 && bn <= 0) - return false; + var an = Vector4.Dot(new(a, 1), NearPlane); + var bn = Vector4.Dot(new(b, 1), NearPlane); + if (an >= 0 && bn >= 0) + return false; // line fully behind near plane - if (an < 0 || bn < 0) + if (an > 0 || bn > 0) { var ab = b - a; - var abn = SharpDX.Vector3.Dot(ab, new(n.X, n.Y, n.Z)); + var abn = Vector3.Dot(ab, new(NearPlane.X, NearPlane.Y, NearPlane.Z)); var t = -an / abn; - if (an < 0) - a = a + t * ab; + var p = a + t * ab; + if (an > 0) + a = p; else - b = a + t * ab; + b = p; } return true; } - private Vector2 WorldToScreen(SharpDX.Vector3 w) + private Vector2 WorldToScreen(Vector3 w) { - var p = SharpDX.Vector3.TransformCoordinate(w, ViewProj); - return new Vector2(0.5f * ViewportSize.X * (1 + p.X), 0.5f * ViewportSize.Y * (1 - p.Y)) + ImGuiHelpers.MainViewport.Pos; + var pp = Vector4.Transform(w, ViewProj); + var iw = 1 / pp.W; + return new Vector2(0.5f * ViewportSize.X * (1 + pp.X * iw), 0.5f * ViewportSize.Y * (1 - pp.Y * iw)) + ImGuiHelpers.MainViewport.Pos; } private static IEnumerable<(T, T)> AdjacentPairs(IEnumerable v) where T : struct diff --git a/vnavmesh/Debug/DebugGameCollision.cs b/vnavmesh/Debug/DebugGameCollision.cs index 4272fc1..76a319f 100644 --- a/vnavmesh/Debug/DebugGameCollision.cs +++ b/vnavmesh/Debug/DebugGameCollision.cs @@ -1,18 +1,18 @@ using Dalamud.Hooking; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; -using Dalamud.Memory; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.LayoutEngine; using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision.Math; -using FFXIVClientStructs.FFXIV.Common.Math; using ImGuiNET; using Navmesh.Render; using System; using System.Collections.Generic; using System.Text; +using Matrix4x4 = System.Numerics.Matrix4x4; +using Vector3 = System.Numerics.Vector3; using Vector4 = System.Numerics.Vector4; namespace Navmesh.Debug; @@ -276,26 +276,27 @@ private void DrawSceneRaycasts(SceneWrapper* s, int index) return; } - var clipPos = new SharpDX.Vector3(2 * screenPos.X / _dd.ViewportSize.X - 1, 1 - 2 * screenPos.Y / _dd.ViewportSize.Y, 1); - var invViewProj = SharpDX.Matrix.Invert(_dd.ViewProj); - var cameraWorldPos = _dd.CameraWorld.Row4.ToSystem(); - var cameraPosAtPlane = SharpDX.Vector3.TransformCoordinate(clipPos, invViewProj).ToSystem(); - var dir = System.Numerics.Vector3.Normalize(cameraPosAtPlane - new System.Numerics.Vector3(cameraWorldPos.X, cameraWorldPos.Y, cameraWorldPos.Z)); + var clipPos = new Vector3(2 * screenPos.X / _dd.ViewportSize.X - 1, 1 - 2 * screenPos.Y / _dd.ViewportSize.Y, 1); + Matrix4x4.Invert(_dd.ViewProj, out var invViewProj); + var cameraPosAtPlaneP = Vector4.Transform(clipPos, invViewProj); + var cameraPosAtPlane = new Vector3(cameraPosAtPlaneP.X / cameraPosAtPlaneP.W, cameraPosAtPlaneP.Y / cameraPosAtPlaneP.W, cameraPosAtPlaneP.Z / cameraPosAtPlaneP.W); + var dir = Vector3.Normalize(cameraPosAtPlane - _dd.Origin); _tree.LeafNode($"Mouse pos: screen={screenPos}, clip={clipPos}, dir={dir}"); float maxDist = 100000; var filter = new RaycastMaterialFilter() { Mask = _materialMask.Raw, Value = _materialId.Raw }; var res = new RaycastHit(); - var arg = new RaycastParams() { Origin = &cameraWorldPos, Direction = &dir, MaxDistance = &maxDist, MaterialFilter = &filter }; + var sphere = new Vector4(_dd.Origin, 1); + var arg = new RaycastParams() { Origin = &sphere, Direction = &dir, MaxDistance = &maxDist, MaterialFilter = &filter }; if (s->Raycast(&res, _shownLayers.Raw, &arg)) { - _tree.LeafNode($"Raycast: {cameraWorldPos} + {res.Distance} = {res.Point}"); + _tree.LeafNode($"Raycast: {_dd.Origin} + {res.Distance} = {res.Point}"); var ab = res.V2 - res.V1; var ac = res.V3 - res.V1; - var normal = Vector3.Cross(ab, ac).Normalized; + var normal = Vector3.Normalize(Vector3.Cross(ab, ac)); _tree.LeafNode($"Normal: {normal} (slope={Angle.Acos(normal.Y)})"); _tree.LeafNode($"Material: {res.Material:X}"); - DrawCollider((Collider*)res.Object); - VisualizeCollider((Collider*)res.Object, _materialId, _materialMask); + DrawCollider(res.Object); + VisualizeCollider(res.Object, _materialId, _materialMask); _dd.DrawWorldLine(res.V1, res.V2, 0xff0000ff, 2); _dd.DrawWorldLine(res.V2, res.V3, 0xff0000ff, 2); _dd.DrawWorldLine(res.V3, res.V1, 0xff0000ff, 2); diff --git a/vnavmesh/Render/EffectMesh.cs b/vnavmesh/Render/EffectMesh.cs index 9090cae..9096caf 100644 --- a/vnavmesh/Render/EffectMesh.cs +++ b/vnavmesh/Render/EffectMesh.cs @@ -1,5 +1,4 @@ using FFXIVClientStructs.FFXIV.Common.Component.BGCollision.Math; -using SharpDX; using SharpDX.D3DCompiler; using SharpDX.Direct3D; using SharpDX.Direct3D11; @@ -8,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Matrix4x4 = System.Numerics.Matrix4x4; using Vector3 = System.Numerics.Vector3; using Vector4 = System.Numerics.Vector4; @@ -20,7 +20,7 @@ public record struct Mesh(int FirstVertex, int FirstPrimitive, int NumPrimitives [StructLayout(LayoutKind.Sequential)] public struct Constants { - public Matrix ViewProj; + public Matrix4x4 ViewProj; public Vector3 CameraPos; public float LightingWorldYThreshold; // to match recast demo, this should be equal to cos of max walkable angle } @@ -260,7 +260,7 @@ public void Dispose() public void UpdateConstants(RenderContext ctx, Constants consts) { - consts.ViewProj.Transpose(); + consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj); ctx.Context.UpdateSubresource(ref consts, _constantBuffer); } diff --git a/vnavmesh/Render/RenderTarget.cs b/vnavmesh/Render/RenderTarget.cs index 2841628..50334ee 100644 --- a/vnavmesh/Render/RenderTarget.cs +++ b/vnavmesh/Render/RenderTarget.cs @@ -1,8 +1,8 @@ -using SharpDX; -using SharpDX.Direct3D; +using SharpDX.Direct3D; using SharpDX.Direct3D11; using SharpDX.DXGI; using System; +using System.Numerics; namespace Navmesh.Render;