1
0
Fork 0
forked from aly/qstbak

Compare commits

...
Sign in to create a new pull request.

46 commits
main ... main

Author SHA1 Message Date
911ed68baa muffin v7.5.12 2026-08-19 13:19:57 +10:00
3102923ce2 muffin v7.5.11 2026-08-17 20:29:32 +10:00
addf2d530f muffin v7.5.6 2026-08-17 20:27:25 +10:00
6266f9e6b7 muffin v7.5.5 2026-08-17 20:25:32 +10:00
a8f2c1df37 muffin v7.5.4 2026-06-06 23:47:07 +10:00
40d48d62b0 muffin v7.5.3 2026-05-13 02:26:07 +10:00
41e27710e2 muffin v7.5.2 2026-05-02 10:19:05 +10:00
965a736f84 muffin v7.5.1 2026-05-01 08:59:48 +10:00
a6481e7b9a muffin v7.5.0 2026-05-01 05:17:35 +10:00
afafd5e377 muffin v7.4.19 2026-03-26 14:57:15 +10:00
f82b9ce2a2 muffin v7.4.18 2026-03-26 14:56:12 +10:00
53aa9fdee8 muffin v7.4.17 2026-03-01 13:56:46 +10:00
e21aa9ca07 muffin v7.4.16 2026-03-01 13:55:33 +10:00
0b1b2d38c7 muffin v7.4.15 2026-03-01 00:29:23 +10:00
9bf3dbdf69 muffin v7.4.14 2026-02-27 22:56:00 +10:00
78e48294c5 muffin v7.4.13 2026-02-27 22:54:28 +10:00
0f9f445830 muffin v7.4.12 2026-02-27 22:50:51 +10:00
e3e5a401c3 muffin v7.4.11 2026-02-03 08:57:51 +10:00
b8dd142c23 muffin v7.4.10 2026-01-19 08:31:23 +10:00
2df81c5d15 muffin v7.4.9 2026-01-01 09:34:51 +10:00
a9e1cdc5f1 muffin v7.4.8 2025-12-29 11:41:48 +10:00
63f975ff4f muffin v7.4.7 2025-12-28 12:35:39 +10:00
1cc65e495d muffin v7.4.6 2025-12-24 05:01:16 +10:00
bb09805213 muffin v7.4.5 2025-12-23 10:08:49 +10:00
8461a561d3 muffin v7.4.4 2025-12-23 09:48:23 +10:00
5791858984 muffin v7.4.3 2025-12-22 00:05:42 +10:00
bddffe21ae muffin v7.4.2 2025-12-21 06:49:12 +10:00
7ae14cf8b8 muffin v7.4.1 2025-12-20 04:56:36 +10:00
a4175abacd muffin v7.4 2025-12-18 00:49:57 +10:00
8a7847ff37 muffin v7.38.9 2025-12-07 10:55:56 +10:00
ada27cf05b qstcompanion v1.0.6 2025-12-07 10:54:53 +10:00
5e1e1decc5 qstcompanion v1.0.5 2025-12-04 04:40:50 +10:00
52daefcfd7 qstcompanion v1.0.4 2025-12-04 04:40:27 +10:00
ca577ad208 qstcompanion v1.0.3 2025-12-04 04:40:03 +10:00
ca78750ba5 qstcompanion v1.0.2 2025-12-04 04:39:15 +10:00
44c67ab71b qstcompanion v1.0.1 2025-12-04 04:39:08 +10:00
3e10cbbbf2 muffin v7.38.8 2025-11-30 10:36:46 +10:00
5e2d8f648b muffin v7.38.7 2025-11-26 06:52:32 +10:00
317800fb39 muffin v7.38.6 2025-11-25 11:31:56 +10:00
ac85eea230 muffin v7.38.5 2025-11-24 14:55:42 +10:00
7177a5440c muffin v7.38.4 2025-11-23 16:08:02 +10:00
bbc394c386 muffin v7.38.2 2025-11-19 12:01:37 +10:00
0336b9c9e9 muffin v7.38.1 2025-11-18 10:16:09 +10:00
e5b98b3d57 muffin v7.38 2025-11-17 11:31:27 +10:00
411c0bbe76 muffin v6.38 2025-11-09 09:25:53 +10:00
98989e8a70 muffin v6.37 2025-11-09 04:17:00 +10:00
812 changed files with 125123 additions and 508426 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
AssemblyInfo.cs
Solution.sln
.vs
**/bin/
**/obj/

View file

@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Auto)]
[InlineArray(36)]
internal struct _003C_003Ey__InlineArray36<T>
{
}

View file

View file

@ -0,0 +1,26 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Auspex.Lighting.Internal;
[StructLayout(LayoutKind.Explicit, Size = 160)]
internal struct GameLightData
{
[FieldOffset(0)]
public unsafe GameLightVTable* VirtualTable;
[FieldOffset(80)]
public LightTransform Transform;
[FieldOffset(112)]
public Vector3 Scale;
[FieldOffset(136)]
public byte Flags00;
[FieldOffset(137)]
public byte Flags01;
[FieldOffset(144)]
public unsafe LightRenderData* RenderObject;
}

View file

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Auspex.Lighting.Internal;
[StructLayout(LayoutKind.Explicit)]
internal struct GameLightVTable
{
[FieldOffset(0)]
public unsafe delegate* unmanaged<GameLightData*, bool, void> Destructor;
[FieldOffset(8)]
public unsafe delegate* unmanaged<GameLightData*, void> Cleanup;
}

View file

@ -0,0 +1,56 @@
namespace Auspex.Lighting.Internal;
internal static class LightNativeBridge
{
public unsafe delegate GameLightData* SpawnDelegate(GameLightData* light);
public unsafe delegate void CreateDelegate(GameLightData* light);
public unsafe delegate void FinalizeDelegate(GameLightData* light);
public unsafe delegate void UpdateCullingDelegate(GameLightData* light);
public unsafe delegate void UpdateMaterialDelegate(GameLightData* light);
public unsafe delegate void UpdateRangeDelegate(LightRenderData* renderData);
public const string SpawnSig = "E8 ?? ?? ?? ?? 48 89 84 ?? ?? ?? ?? ?? 48 85 C0 0F ?? ?? ?? ?? ?? 48 8B C8";
public static SpawnDelegate Spawn;
public const string CreateSig = "E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D3 E8 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 40 0F";
public static CreateDelegate Create;
public const string FinalizeSig = "F6 41 38 01 ?? ?? 48 8B ?? ?? ?? ?? ?? 48";
public new static FinalizeDelegate Finalize;
public const string UpdateCullingSig = "48 89 5C 24 ?? 57 48 83 EC 40 48 8B B9 ?? ?? ?? ??";
public static UpdateCullingDelegate UpdateCulling;
public const string UpdateMaterialSig = "40 53 48 83 EC 20 0F B6 81 ?? ?? ?? ?? 48 8B D9 A8 04 75 45 0C 04 B2 05";
public static UpdateMaterialDelegate UpdateMaterial;
public const string UpdateRangeSig = "E8 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? 48 8D 55";
public static UpdateRangeDelegate UpdateRange;
internal static void Initialize()
{
SpawnDelegate spawn = SigScanHelper.Scan<SpawnDelegate>("E8 ?? ?? ?? ?? 48 89 84 ?? ?? ?? ?? ?? 48 85 C0 0F ?? ?? ?? ?? ?? 48 8B C8", "Spawn");
CreateDelegate create = SigScanHelper.Scan<CreateDelegate>("E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D3 E8 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 40 0F", "Create");
FinalizeDelegate finalize = SigScanHelper.Scan<FinalizeDelegate>("F6 41 38 01 ?? ?? 48 8B ?? ?? ?? ?? ?? 48", "Finalize");
UpdateCullingDelegate updateCulling = SigScanHelper.Scan<UpdateCullingDelegate>("48 89 5C 24 ?? 57 48 83 EC 40 48 8B B9 ?? ?? ?? ??", "UpdateCulling");
UpdateMaterialDelegate updateMaterial = SigScanHelper.Scan<UpdateMaterialDelegate>("40 53 48 83 EC 20 0F B6 81 ?? ?? ?? ?? 48 8B D9 A8 04 75 45 0C 04 B2 05", "UpdateMaterial");
UpdateRangeDelegate updateRange = SigScanHelper.Scan<UpdateRangeDelegate>("E8 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? 48 8D 55", "UpdateRange");
Spawn = spawn;
Create = create;
Finalize = finalize;
UpdateCulling = updateCulling;
UpdateMaterial = updateMaterial;
UpdateRange = updateRange;
}
}

View file

@ -0,0 +1,47 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Auspex.Lighting.Internal;
[StructLayout(LayoutKind.Explicit, Size = 160)]
internal struct LightRenderData
{
[FieldOffset(24)]
public AxLightFlags Flags;
[FieldOffset(28)]
public AxLightType EmissionType;
[FieldOffset(32)]
public unsafe LightTransform* Transform;
[FieldOffset(40)]
public Vector3 Color;
[FieldOffset(52)]
public float Intensity;
[FieldOffset(96)]
public float ShadowPlaneNear;
[FieldOffset(100)]
public float ShadowPlaneFar;
[FieldOffset(104)]
public AxFalloffType FalloffType;
[FieldOffset(128)]
public float Falloff;
[FieldOffset(132)]
public float LightAngle;
[FieldOffset(136)]
public float FalloffAngle;
[FieldOffset(140)]
public float Range;
[FieldOffset(144)]
public float CharacterShadowRange;
}

View file

@ -0,0 +1,17 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Auspex.Lighting.Internal;
[StructLayout(LayoutKind.Explicit, Size = 48)]
internal struct LightTransform
{
[FieldOffset(0)]
public Vector3 Position;
[FieldOffset(16)]
public Quaternion Rotation;
[FieldOffset(32)]
public Vector3 Scale;
}

View file

@ -0,0 +1,205 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
namespace Auspex.Lighting.Internal;
internal sealed class NativeLightHandle : IDisposable
{
private unsafe GameLightData* data;
private Vector3 position;
private Quaternion rotation;
private Vector3 color;
private float intensity;
private float range;
private float lightAngle;
private float falloffAngle;
private AxLightFlags flags;
private AxFalloffType falloffType;
private float falloffPower;
private float shadowNear;
private float shadowFar;
private float charaShadowRange;
public unsafe static NativeLightHandle Create(Vector3 position, AxLightType lightType, AxLightConfig config)
{
GameLightData* ptr = IMemorySpace.GetDefaultSpace()->Malloc<GameLightData>(8uL);
if (LightNativeBridge.Spawn(ptr) == null)
{
IMemorySpace.Free(ptr, (ulong)sizeof(GameLightData));
throw new InvalidOperationException("Failed to spawn game light");
}
LightNativeBridge.Create(ptr);
LightNativeBridge.Finalize(ptr);
((long*)ptr)[7] |= 2L;
LightRenderData* renderObject = ptr->RenderObject;
if (renderObject == null)
{
IMemorySpace.Free(ptr, (ulong)sizeof(GameLightData));
throw new InvalidOperationException("Game did not create RenderObject for light");
}
renderObject->Transform = &ptr->Transform;
renderObject->EmissionType = lightType;
renderObject->Color = config.Color;
renderObject->Intensity = config.Intensity;
renderObject->Range = config.Range;
renderObject->FalloffType = config.FalloffType;
renderObject->Falloff = config.FalloffPower;
renderObject->LightAngle = config.LightAngle;
renderObject->FalloffAngle = config.FalloffAngle;
renderObject->CharacterShadowRange = config.CharaShadowRange;
renderObject->ShadowPlaneNear = config.ShadowNear;
renderObject->ShadowPlaneFar = config.ShadowFar;
renderObject->Flags = config.Flags;
LightNativeBridge.UpdateRange(renderObject);
Quaternion quaternion = config.Rotation ?? Quaternion.Identity;
Unsafe.Write<Vector3>((void*)(&ptr->Transform.Position), position);
Unsafe.Write<Quaternion>((void*)(&ptr->Transform.Rotation), quaternion);
ptr->Scale = Vector3.One;
return new NativeLightHandle
{
data = ptr,
position = position,
rotation = quaternion,
color = config.Color,
intensity = config.Intensity,
range = config.Range,
lightAngle = config.LightAngle,
falloffAngle = config.FalloffAngle,
flags = config.Flags,
falloffType = config.FalloffType,
falloffPower = config.FalloffPower,
shadowNear = config.ShadowNear,
shadowFar = config.ShadowFar,
charaShadowRange = config.CharaShadowRange
};
}
public unsafe void UpdatePosition(Vector3 newPosition)
{
if (data != null && !(position == newPosition))
{
position = newPosition;
Unsafe.Write<Vector3>((void*)(&data->Transform.Position), newPosition);
}
}
public unsafe void UpdateRotation(Quaternion newRotation)
{
if (data != null && !(rotation == newRotation))
{
rotation = newRotation;
Unsafe.Write<Quaternion>((void*)(&data->Transform.Rotation), newRotation);
}
}
public unsafe void UpdateColor(Vector3 newColor)
{
if (data != null && data->RenderObject != null && !(color == newColor))
{
color = newColor;
data->RenderObject->Color = newColor;
}
}
public unsafe void UpdateIntensity(float newIntensity)
{
if (data != null && data->RenderObject != null && intensity != newIntensity)
{
intensity = newIntensity;
data->RenderObject->Intensity = newIntensity;
}
}
public unsafe void UpdateRange(float newRange)
{
if (data != null && data->RenderObject != null && range != newRange)
{
range = newRange;
data->RenderObject->Range = newRange;
LightNativeBridge.UpdateRange(data->RenderObject);
}
}
public unsafe void UpdateFlags(AxLightFlags newFlags)
{
if (data != null && data->RenderObject != null && flags != newFlags)
{
flags = newFlags;
data->RenderObject->Flags = newFlags;
}
}
public unsafe void UpdateAngles(float newLightAngle, float newFalloffAngle)
{
if (data != null && data->RenderObject != null && (lightAngle != newLightAngle || falloffAngle != newFalloffAngle))
{
lightAngle = newLightAngle;
falloffAngle = newFalloffAngle;
data->RenderObject->LightAngle = newLightAngle;
data->RenderObject->FalloffAngle = newFalloffAngle;
}
}
public unsafe void UpdateFalloff(AxFalloffType newFalloffType, float newFalloffPower)
{
if (data != null && data->RenderObject != null && (falloffType != newFalloffType || falloffPower != newFalloffPower))
{
falloffType = newFalloffType;
falloffPower = newFalloffPower;
data->RenderObject->FalloffType = newFalloffType;
data->RenderObject->Falloff = newFalloffPower;
}
}
public unsafe void UpdateShadow(float newShadowNear, float newShadowFar, float newCharaShadowRange)
{
if (data != null && data->RenderObject != null && (shadowNear != newShadowNear || shadowFar != newShadowFar || charaShadowRange != newCharaShadowRange))
{
shadowNear = newShadowNear;
shadowFar = newShadowFar;
charaShadowRange = newCharaShadowRange;
data->RenderObject->ShadowPlaneNear = newShadowNear;
data->RenderObject->ShadowPlaneFar = newShadowFar;
data->RenderObject->CharacterShadowRange = newCharaShadowRange;
}
}
internal unsafe void Tick()
{
if (data != null)
{
LightNativeBridge.UpdateCulling(data);
LightNativeBridge.UpdateMaterial(data);
}
}
public unsafe void Dispose()
{
if (data != null)
{
GameLightVTable* virtualTable = data->VirtualTable;
if (virtualTable != null)
{
virtualTable->Cleanup(data);
virtualTable->Destructor(data, false);
}
data = null;
GC.SuppressFinalize(this);
}
}
}

View file

@ -0,0 +1,174 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class ClipZoneShader : IDisposable
{
public struct Constants
{
public Vector2 ViewportSize;
}
public struct Instance
{
public Vector2 Min;
public Vector2 Max;
}
private readonly FrameRenderContext _ctx;
private readonly GpuBuffer<Instance> _buffer;
private GpuBuffer<Instance>.Builder? _builder;
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11InputLayout* _il;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
public bool HasPending => _builder != null;
public unsafe ClipZoneShader(FrameRenderContext ctx, int maxRects)
{
_ctx = ctx;
_buffer = new GpuBuffer<Instance>("ClipZone", ctx, maxRects, 1u, dynamic: true);
ReadOnlySpan<byte> source = "cbuffer Constants : register(b0)\n{\n float2 viewportSize;\n};\n\nstruct Rect\n{\n float2 rmin : RECTMIN;\n float2 rmax : RECTMAX;\n};\n\nstruct VSOutput\n{\n float4 pos : SV_POSITION;\n};\n\nVSOutput vs(in Rect r, uint vid : SV_VertexID)\n{\n VSOutput o;\n float2 px = float2(\n (vid & 1) ? r.rmax.x : r.rmin.x,\n (vid & 2) ? r.rmax.y : r.rmin.y);\n float2 ndc = float2(\n px.x / viewportSize.x * 2.0 - 1.0,\n 1.0 - px.y / viewportSize.y * 2.0);\n o.pos = float4(ndc, 0, 1);\n return o;\n}\n\nvoid ps(VSOutput input)\n{\n}"u8;
TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "ClipZone VS");
TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "ClipZone PS");
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
_vs = vs;
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
_ps = ps;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = 16u,
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 4u
};
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
_constantBuffer = constantBuffer;
fixed (byte* semanticName = "RECTMIN"u8)
{
fixed (byte* semanticName2 = "RECTMAX"u8)
{
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[2];
*ptr = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName2,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ID3D11InputLayout* il = default(ID3D11InputLayout*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 2u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
_il = il;
}
}
blob->Release();
blob2->Release();
}
public unsafe void Dispose()
{
_builder?.Dispose();
_buffer.Dispose();
if (_constantBuffer != null)
{
_constantBuffer->Release();
_constantBuffer = null;
}
if (_il != null)
{
_il->Release();
_il = null;
}
if (_vs != null)
{
_vs->Release();
_vs = null;
}
if (_ps != null)
{
_ps->Release();
_ps = null;
}
GC.SuppressFinalize(this);
}
public unsafe void UpdateConstants(Constants consts)
{
_ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
}
public void Add(Vector2 min, Vector2 max)
{
if (_builder == null)
{
_builder = _buffer.Map(_ctx);
}
Instance item = new Instance
{
Min = min,
Max = max
};
_builder.Add(ref item);
}
public unsafe void Flush()
{
if (_builder != null)
{
_builder.Dispose();
_builder = null;
Bind();
ID3D11Buffer* buffer = _buffer.Buffer;
uint elementSize = (uint)_buffer.ElementSize;
uint num = 0u;
_ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num);
_ctx.Context->DrawInstanced(4u, (uint)_buffer.CurElements, 0u, 0u);
}
}
public void Discard()
{
if (_builder != null)
{
_builder.Dispose();
_builder = null;
}
}
private unsafe void Bind()
{
_ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
_ctx.Context->IASetInputLayout(_il);
_ctx.Context->VSSetShader(_vs, null, 0u);
ID3D11Buffer* constantBuffer = _constantBuffer;
_ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
_ctx.Context->PSSetShader(_ps, null, 0u);
_ctx.Context->GSSetShader(null, null, 0u);
}
}

View file

@ -0,0 +1,20 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using Dalamud.Bindings.ImGui;
namespace Auspex.Rendering.Direct3D;
internal static class ColorConversions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUint(this Vector4 color)
{
return ImGui.ColorConvertFloat4ToU32(color);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 ToVector4(this uint color)
{
return ImGui.ColorConvertU32ToFloat4(color);
}
}

View file

@ -0,0 +1,787 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Auspex.Rendering.Direct3D;
internal sealed class Direct3DRenderer : IDisposable
{
public const int MAX_FANS = 2048;
public const int MAX_TRIS = 65536;
public const int MAX_STROKE_SEGMENTS = 245760;
public const int MAX_CLIP_ZONES = 3072;
public const int MAX_TEXTURED_QUADS = 256;
private unsafe ID3D11DepthStencilState* _clipZoneDSS;
private unsafe ID3D11DepthStencilState* _shapeDSS;
private readonly TriangleFillShader.Data _triFillDynamicData;
private TriangleFillShader.Data.Builder? _triFillDynamicBuilder;
private readonly FanFillShader.Data _fanFillDynamicData;
private FanFillShader.Data.Builder? _fanFillDynamicBuilder;
private readonly StrokeShader.Data _strokeDynamicData;
private StrokeShader.Data.Builder? _strokeDynamicBuilder;
private readonly QuadBlitter.Data _texturedQuadDynamicData;
private QuadBlitter.Data.Builder? _texturedQuadDynamicBuilder;
internal readonly List<Vector3> SharedPath = new List<Vector3>();
private unsafe ID3D11RasterizerState* _rasterizerState;
public FrameRenderContext FrameRenderContext { get; init; } = new FrameRenderContext();
internal FrameRenderTarget? FrameRenderTarget { get; private set; }
public TriangleFillShader TriangleFillShader { get; init; }
public FanFillShader FanFillShader { get; init; }
public StrokeShader StrokeShader { get; init; }
public QuadBlitter QuadBlitter { get; init; }
public FullscreenPassShader? FSP { get; init; }
public ClipZoneShader ClipZone { get; init; }
internal SceneDepthCapture SceneDepth { get; init; }
public Matrix4x4 ViewProj { get; private set; }
public Vector2 ViewportSize { get; private set; }
public Vector3 CameraRight { get; private set; }
public Vector3 CameraUp { get; private set; }
public bool FanDegraded { get; private set; }
public bool StrokeDegraded { get; private set; }
public bool FSPDegraded { get; private set; }
public bool TexturedQuadDegraded { get; private set; }
internal HighResFontAtlas? WorldFont { get; private set; }
public Direct3DRenderer()
{
try
{
FanFillShader = new FanFillShader(FrameRenderContext);
}
catch (Exception exception)
{
AuspexService.Log.Error(exception, "[Auspex] Failed to compile fan shader; starting in degraded mode.");
FanDegraded = true;
}
_fanFillDynamicData = new FanFillShader.Data(FrameRenderContext, FanDegraded ? 1 : 2048, dynamic: true);
try
{
StrokeShader = new StrokeShader(FrameRenderContext);
}
catch (Exception exception2)
{
AuspexService.Log.Error(exception2, "[Auspex] Failed to compile stroke shader; starting in degraded mode.");
StrokeDegraded = true;
}
_strokeDynamicData = new StrokeShader.Data(FrameRenderContext, StrokeDegraded ? 1 : 245760, dynamic: true);
try
{
QuadBlitter = new QuadBlitter(FrameRenderContext);
}
catch (Exception exception3)
{
AuspexService.Log.Error(exception3, "[Auspex] Failed to compile textured quad shader; starting in degraded mode.");
TexturedQuadDegraded = true;
}
_texturedQuadDynamicData = new QuadBlitter.Data(FrameRenderContext, TexturedQuadDegraded ? 1 : 1536, dynamic: true);
if (!TexturedQuadDegraded)
{
try
{
WorldFont = new HighResFontAtlas(FrameRenderContext);
}
catch (Exception exception4)
{
AuspexService.Log.Error(exception4, "[Auspex] Failed to create high-res world text font; world text will use low-res fallback.");
}
}
try
{
TriangleFillShader = new TriangleFillShader(FrameRenderContext);
_triFillDynamicData = new TriangleFillShader.Data(FrameRenderContext, 65536 + (FanDegraded ? 737280 : 0), dynamic: true);
}
catch
{
Dispose();
throw;
}
try
{
FSP = new FullscreenPassShader(FrameRenderContext);
}
catch (Exception exception5)
{
AuspexService.Log.Error(exception5, "[Auspex] Failed to compile FSP shader; starting in degraded mode.");
FSPDegraded = true;
}
SceneDepth = new SceneDepthCapture();
ClipZone = new ClipZoneShader(FrameRenderContext, 3072);
CreateRasterizerState();
CreateDepthStencilStates();
}
private unsafe void CreateRasterizerState()
{
D3D11_RASTERIZER_DESC d3D11_RASTERIZER_DESC = new D3D11_RASTERIZER_DESC
{
FillMode = D3D11_FILL_MODE.D3D11_FILL_SOLID,
CullMode = D3D11_CULL_MODE.D3D11_CULL_NONE,
DepthClipEnable = true,
ScissorEnable = true
};
ID3D11RasterizerState* rasterizerState = default(ID3D11RasterizerState*);
Marshal.ThrowExceptionForHR(FrameRenderContext.Device->CreateRasterizerState(&d3D11_RASTERIZER_DESC, &rasterizerState));
_rasterizerState = rasterizerState;
}
private unsafe void CreateDepthStencilStates()
{
D3D11_DEPTH_STENCILOP_DESC d3D11_DEPTH_STENCILOP_DESC = new D3D11_DEPTH_STENCILOP_DESC
{
StencilFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
StencilDepthFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
StencilPassOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_REPLACE,
StencilFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS
};
D3D11_DEPTH_STENCIL_DESC d3D11_DEPTH_STENCIL_DESC = new D3D11_DEPTH_STENCIL_DESC
{
DepthEnable = false,
DepthWriteMask = D3D11_DEPTH_WRITE_MASK.D3D11_DEPTH_WRITE_MASK_ZERO,
StencilEnable = true,
StencilReadMask = byte.MaxValue,
StencilWriteMask = byte.MaxValue,
FrontFace = d3D11_DEPTH_STENCILOP_DESC,
BackFace = d3D11_DEPTH_STENCILOP_DESC
};
ID3D11DepthStencilState* ptr = default(ID3D11DepthStencilState*);
Marshal.ThrowExceptionForHR(FrameRenderContext.Device->CreateDepthStencilState(&d3D11_DEPTH_STENCIL_DESC, &ptr));
_clipZoneDSS = ptr;
D3D11_DEPTH_STENCILOP_DESC d3D11_DEPTH_STENCILOP_DESC2 = new D3D11_DEPTH_STENCILOP_DESC
{
StencilFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
StencilDepthFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
StencilPassOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
StencilFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_EQUAL
};
D3D11_DEPTH_STENCIL_DESC d3D11_DEPTH_STENCIL_DESC2 = new D3D11_DEPTH_STENCIL_DESC
{
DepthEnable = false,
DepthWriteMask = D3D11_DEPTH_WRITE_MASK.D3D11_DEPTH_WRITE_MASK_ZERO,
StencilEnable = true,
StencilReadMask = byte.MaxValue,
StencilWriteMask = 0,
FrontFace = d3D11_DEPTH_STENCILOP_DESC2,
BackFace = d3D11_DEPTH_STENCILOP_DESC2
};
Marshal.ThrowExceptionForHR(FrameRenderContext.Device->CreateDepthStencilState(&d3D11_DEPTH_STENCIL_DESC2, &ptr));
_shapeDSS = ptr;
}
public unsafe void Dispose()
{
WorldFont?.Dispose();
FrameRenderTarget?.Dispose();
_triFillDynamicBuilder?.Dispose();
_triFillDynamicData?.Dispose();
_fanFillDynamicBuilder?.Dispose();
_fanFillDynamicData?.Dispose();
_strokeDynamicBuilder?.Dispose();
_strokeDynamicData?.Dispose();
_texturedQuadDynamicBuilder?.Dispose();
_texturedQuadDynamicData?.Dispose();
if (!FanDegraded)
{
FanFillShader.Dispose();
}
if (!StrokeDegraded)
{
StrokeShader.Dispose();
}
if (!TexturedQuadDegraded)
{
QuadBlitter.Dispose();
}
TriangleFillShader?.Dispose();
FSP?.Dispose();
SceneDepth.Dispose();
ClipZone.Dispose();
if (_clipZoneDSS != null)
{
_clipZoneDSS->Release();
_clipZoneDSS = null;
}
if (_shapeDSS != null)
{
_shapeDSS->Release();
_shapeDSS = null;
}
if (_rasterizerState != null)
{
_rasterizerState->Release();
_rasterizerState = null;
}
FrameRenderContext.Dispose();
GC.SuppressFinalize(this);
}
internal unsafe void BeginFrame()
{
Device* ptr = Device.Instance();
ViewportSize = new Vector2(ptr->Width, ptr->Height);
Control* ptr2 = Control.Instance();
ViewProj = *(Matrix4x4*)(&ptr2->ViewProjectionMatrix);
SceneDepth.Update();
FFXIVClientStructs.FFXIV.Client.Game.Camera* activeCamera = ptr2->CameraManager.GetActiveCamera();
FFXIVClientStructs.FFXIV.Client.Graphics.Render.Camera* ptr3 = ((activeCamera != null) ? activeCamera->SceneCamera.RenderCamera : null);
if (ptr3 != null && Matrix4x4.Invert(ptr3->ProjectionMatrix, out var result))
{
Matrix4x4 matrix4x = ViewProj * result;
CameraRight = Vector3.Normalize(new Vector3(matrix4x.M11, matrix4x.M21, matrix4x.M31));
CameraUp = Vector3.Normalize(new Vector3(matrix4x.M12, matrix4x.M22, matrix4x.M32));
}
else
{
CameraRight = Vector3.UnitX;
CameraUp = Vector3.UnitY;
}
Vector2 pixelToUv = ((SceneDepth.SRV != null) ? (SceneDepth.UvScale / ViewportSize) : Vector2.Zero);
TriangleFillShader.UpdateConstants(FrameRenderContext, new TriangleFillShader.Constants
{
ViewProj = ViewProj,
PixelToUv = pixelToUv
});
if (!FanDegraded)
{
FanFillShader.UpdateConstants(FrameRenderContext, new FanFillShader.Constants
{
ViewProj = ViewProj,
PixelToUv = pixelToUv
});
}
if (!StrokeDegraded)
{
StrokeShader.UpdateConstants(FrameRenderContext, new StrokeShader.Constants
{
ViewProj = ViewProj,
RenderTargetSize = new Vector2(ViewportSize.X, ViewportSize.Y),
PixelToUv = pixelToUv
});
}
if (!TexturedQuadDegraded)
{
QuadBlitter.UpdateConstants(FrameRenderContext, new QuadBlitter.Constants
{
ViewProj = ViewProj
});
}
if (!FSPDegraded)
{
bool flag = AuspexService.Hints.ClipNativeUI && !SceneDepth.IsResolutionScaled;
FSP.UpdateConstants(FrameRenderContext, new FullscreenPassShader.Constants
{
MaxAlpha = AuspexService.Hints.MaxAlphaFraction,
ClipNativeUI = (flag ? 1f : 0f)
});
}
if (FrameRenderTarget == null || FrameRenderTarget.Size != ViewportSize)
{
FrameRenderTarget?.Dispose();
FrameRenderTarget = new FrameRenderTarget(FrameRenderContext, (int)ViewportSize.X, (int)ViewportSize.Y, AuspexService.Hints.AlphaBlendMode);
}
FrameRenderTarget.Bind(FrameRenderContext);
FrameRenderContext.Context->RSSetState(_rasterizerState);
RECT rECT = new RECT
{
left = 0,
top = 0,
right = (int)ViewportSize.X,
bottom = (int)ViewportSize.Y
};
FrameRenderContext.Context->RSSetScissorRects(1u, &rECT);
}
internal void Flush()
{
if (_triFillDynamicBuilder != null)
{
_triFillDynamicBuilder.Dispose();
_triFillDynamicBuilder = null;
TriangleFillShader.Bind(FrameRenderContext);
_triFillDynamicData.DrawAll(FrameRenderContext);
}
if (!FanDegraded && _fanFillDynamicBuilder != null)
{
_fanFillDynamicBuilder.Dispose();
_fanFillDynamicBuilder = null;
FanFillShader.Bind(FrameRenderContext);
_fanFillDynamicData.DrawAll(FrameRenderContext);
}
if (!StrokeDegraded && _strokeDynamicBuilder != null)
{
_strokeDynamicBuilder.Dispose();
_strokeDynamicBuilder = null;
StrokeShader.Bind(FrameRenderContext);
_strokeDynamicData.DrawAll(FrameRenderContext);
}
if (!TexturedQuadDegraded && _texturedQuadDynamicBuilder != null)
{
_texturedQuadDynamicBuilder.Dispose();
_texturedQuadDynamicBuilder = null;
QuadBlitter.Draw(FrameRenderContext, _texturedQuadDynamicData);
}
}
internal unsafe void SetScissorRect(Vector2 min, Vector2 max)
{
Flush();
RECT rECT = new RECT
{
left = (int)min.X,
top = (int)min.Y,
right = (int)max.X,
bottom = (int)max.Y
};
FrameRenderContext.Context->RSSetScissorRects(1u, &rECT);
}
internal unsafe void ClearScissorRect()
{
Flush();
RECT rECT = new RECT
{
left = 0,
top = 0,
right = (int)ViewportSize.X,
bottom = (int)ViewportSize.Y
};
FrameRenderContext.Context->RSSetScissorRects(1u, &rECT);
}
public void AddClipZone(Vector2 min, Vector2 max)
{
ClipZone.Add(min, max);
}
internal unsafe FrameRenderTarget EndFrame()
{
bool num = _triFillDynamicBuilder != null || (!FanDegraded && _fanFillDynamicBuilder != null) || (!StrokeDegraded && _strokeDynamicBuilder != null) || (!TexturedQuadDegraded && _texturedQuadDynamicBuilder != null);
bool hasPending = ClipZone.HasPending;
if (num && SceneDepth.SRV != null)
{
ID3D11DepthStencilView* clipStencilDSV = FrameRenderTarget.ClipStencilDSV;
FrameRenderContext.Context->OMSetRenderTargets(0u, null, clipStencilDSV);
FrameRenderContext.Context->ClearDepthStencilView(clipStencilDSV, 2u, 0f, 0);
if (hasPending)
{
ClipZone.UpdateConstants(new ClipZoneShader.Constants
{
ViewportSize = new Vector2(ViewportSize.X, ViewportSize.Y)
});
FrameRenderContext.Context->OMSetDepthStencilState(_clipZoneDSS, 1u);
ClipZone.Flush();
}
ID3D11RenderTargetView* baseRTV = FrameRenderTarget.BaseRTV;
FrameRenderContext.Context->OMSetRenderTargets(1u, &baseRTV, clipStencilDSV);
FrameRenderContext.Context->OMSetDepthStencilState(_shapeDSS, 0u);
ID3D11ShaderResourceView* sRV = SceneDepth.SRV;
FrameRenderContext.Context->PSSetShaderResources(0u, 1u, &sRV);
}
else
{
ClipZone.Discard();
}
Flush();
if (SceneDepth.SRV != null)
{
ID3D11ShaderResourceView* ptr = null;
FrameRenderContext.Context->PSSetShaderResources(0u, 1u, &ptr);
}
if (!FSPDegraded)
{
Device* ptr2 = Device.Instance();
if (ptr2 != null && ptr2->SwapChain != null && ptr2->SwapChain->BackBuffer != null && ptr2->SwapChain->BackBuffer->D3D11Texture2D != null)
{
ID3D11Texture2D* d3D11Texture2D = (ID3D11Texture2D*)ptr2->SwapChain->BackBuffer->D3D11Texture2D;
FrameRenderTarget.ExecuteFSP(FrameRenderContext, d3D11Texture2D, FSP, AuspexService.Hints.ClipNativeUI && !SceneDepth.IsResolutionScaled);
}
else
{
AuspexService.Log.Warning("[Auspex] Direct3DRenderer.EndFrame: Device or BackBuffer is null; skipping combined pass.");
}
}
else
{
FrameRenderTarget.CopyBaseToProcessed(FrameRenderContext);
}
FrameRenderContext.Execute();
return FrameRenderTarget;
}
public void DrawTriangle(Vector3 a, Vector3 b, Vector3 c, uint colorA, uint colorB, uint colorC, AxDxParams p)
{
TriangleFillShader.Data.Builder triFills = GetTriFills();
triFills.Add(a, colorA.ToVector4(), p);
triFills.Add(b, colorB.ToVector4(), p);
triFills.Add(c, colorC.ToVector4(), p);
}
private void DrawTriangle(Vector3 a, Vector3 b, Vector3 c, Vector4 colorA, Vector4 colorB, Vector4 colorC, AxDxParams p)
{
TriangleFillShader.Data.Builder triFills = GetTriFills();
triFills.Add(a, colorA, p);
triFills.Add(b, colorB, p);
triFills.Add(c, colorC, p);
}
private TriangleFillShader.Data.Builder GetTriFills()
{
return _triFillDynamicBuilder ?? (_triFillDynamicBuilder = _triFillDynamicData.Map(FrameRenderContext));
}
private void DrawTriangleFan(Vector3 center, float innerRadius, float outerRadius, float minAngle, float maxAngle, uint innerColor, uint outerColor, uint numSegments = 0u, AxDxParams p = default(AxDxParams))
{
float num = maxAngle - minAngle;
if (num == 0f)
{
return;
}
if (numSegments == 0)
{
numSegments = (uint)(MathF.Abs(num) * 8f);
}
float num2 = num / (float)numSegments;
Vector4 vector = innerColor.ToVector4();
Vector4 vector2 = outerColor.ToVector4();
Vector3 vector3 = default(Vector3);
for (int i = 0; i <= numSegments; i++)
{
float num3 = (float)Math.PI / 2f + minAngle + (float)i * num2;
Vector3 vector4 = new Vector3(MathF.Cos(num3), 0f, MathF.Sin(num3));
if (i > 0)
{
if (innerRadius > 0f)
{
DrawTriangle(center + innerRadius * vector3, center + outerRadius * vector3, center + outerRadius * vector4, vector, vector2, vector2, p);
DrawTriangle(center + outerRadius * vector4, center + innerRadius * vector4, center + innerRadius * vector3, vector2, vector, vector, p);
}
else
{
DrawTriangle(center, center + outerRadius * vector3, center + outerRadius * vector4, vector, vector2, vector2, p);
}
}
vector3 = vector4;
}
}
internal void DrawTriangleFanGradient<T>(Vector3 center, float innerRadius, float outerRadius, float minAngle, float maxAngle, T gradient, uint numSegments = 0u, AxDxParams p = default(AxDxParams)) where T : IPctGradient
{
float num = maxAngle - minAngle;
if (num == 0f)
{
return;
}
if (numSegments == 0)
{
numSegments = (uint)(MathF.Abs(num) * 8f);
}
float num2 = num / (float)numSegments;
Vector3 vector = default(Vector3);
for (int i = 0; i <= numSegments; i++)
{
float num3 = (float)Math.PI / 2f + minAngle + (float)i * num2;
Vector3 vector2 = new Vector3(MathF.Cos(num3), 0f, MathF.Sin(num3));
if (i > 0)
{
if (innerRadius > 0f)
{
Vector3 vector3 = center + innerRadius * vector;
Vector3 vector4 = center + outerRadius * vector;
Vector3 vector5 = center + outerRadius * vector2;
Vector3 vector6 = center + innerRadius * vector2;
DrawTriangle(vector3, vector4, vector5, gradient.ColorAt(vector3).ToVector4(), gradient.ColorAt(vector4).ToVector4(), gradient.ColorAt(vector5).ToVector4(), p);
DrawTriangle(vector5, vector6, vector3, gradient.ColorAt(vector5).ToVector4(), gradient.ColorAt(vector6).ToVector4(), gradient.ColorAt(vector3).ToVector4(), p);
}
else
{
Vector3 vector7 = center + outerRadius * vector;
Vector3 vector8 = center + outerRadius * vector2;
DrawTriangle(center, vector7, vector8, gradient.ColorAt(center).ToVector4(), gradient.ColorAt(vector7).ToVector4(), gradient.ColorAt(vector8).ToVector4(), p);
}
}
vector = vector2;
}
}
internal void DrawTerrainFan(Vector3 origin, float innerRadius, float outerRadius, float minAngle, float maxAngle, uint innerColor, uint outerColor, float resolution, float yOffset, uint numSegments = 0u, AxDxParams p = default(AxDxParams))
{
float num = maxAngle - minAngle;
if (num == 0f)
{
return;
}
uint num2 = ((numSegments != 0) ? numSegments : ((uint)MathF.Ceiling(MathF.Abs(num) * outerRadius / resolution)));
if (num2 == 0)
{
num2 = 1u;
}
int num3 = Math.Max(1, (int)MathF.Ceiling((outerRadius - innerRadius) / resolution));
float num4 = num / (float)num2;
Vector4 value = innerColor.ToVector4();
Vector4 value2 = outerColor.ToVector4();
Vector3 a = ((innerRadius == 0f) ? TerrainVertex(origin, Vector3.Zero, 0f, yOffset) : default(Vector3));
float num5 = (float)Math.PI / 2f + minAngle;
Vector3 direction = new Vector3(MathF.Cos(num5), 0f, MathF.Sin(num5));
for (int i = 1; i <= num2; i++)
{
float num6 = (float)Math.PI / 2f + minAngle + (float)i * num4;
Vector3 vector = new Vector3(MathF.Cos(num6), 0f, MathF.Sin(num6));
for (int j = 0; j < num3; j++)
{
float num7 = innerRadius + (outerRadius - innerRadius) * (float)j / (float)num3;
float radius = innerRadius + (outerRadius - innerRadius) * (float)(j + 1) / (float)num3;
float amount = (float)j / (float)num3;
float amount2 = (float)(j + 1) / (float)num3;
Vector4 vector2 = Vector4.Lerp(value, value2, amount);
Vector4 vector3 = Vector4.Lerp(value, value2, amount2);
if (num7 > 0f)
{
Vector3 vector4 = TerrainVertex(origin, direction, num7, yOffset);
Vector3 b = TerrainVertex(origin, direction, radius, yOffset);
Vector3 vector5 = TerrainVertex(origin, vector, radius, yOffset);
Vector3 b2 = TerrainVertex(origin, vector, num7, yOffset);
DrawTriangle(vector4, b, vector5, vector2, vector3, vector3, p);
DrawTriangle(vector5, b2, vector4, vector3, vector2, vector2, p);
}
else
{
Vector3 b3 = TerrainVertex(origin, direction, radius, yOffset);
Vector3 c = TerrainVertex(origin, vector, radius, yOffset);
DrawTriangle(a, b3, c, vector2, vector3, vector3, p);
}
}
direction = vector;
}
}
internal void DrawTerrainFanGradient<T>(Vector3 origin, float innerRadius, float outerRadius, float minAngle, float maxAngle, T gradient, float resolution, float yOffset, uint numSegments = 0u, AxDxParams p = default(AxDxParams)) where T : IPctGradient
{
float num = maxAngle - minAngle;
if (num == 0f)
{
return;
}
uint num2 = ((numSegments != 0) ? numSegments : ((uint)MathF.Ceiling(MathF.Abs(num) * outerRadius / resolution)));
if (num2 == 0)
{
num2 = 1u;
}
int num3 = Math.Max(1, (int)MathF.Ceiling((outerRadius - innerRadius) / resolution));
float num4 = num / (float)num2;
Vector3 vector = ((innerRadius == 0f) ? TerrainVertex(origin, Vector3.Zero, 0f, yOffset) : default(Vector3));
Vector4 colorA = ((innerRadius == 0f) ? gradient.ColorAt(vector).ToVector4() : default(Vector4));
float num5 = (float)Math.PI / 2f + minAngle;
Vector3 direction = new Vector3(MathF.Cos(num5), 0f, MathF.Sin(num5));
for (int i = 1; i <= num2; i++)
{
float num6 = (float)Math.PI / 2f + minAngle + (float)i * num4;
Vector3 vector2 = new Vector3(MathF.Cos(num6), 0f, MathF.Sin(num6));
for (int j = 0; j < num3; j++)
{
float num7 = innerRadius + (outerRadius - innerRadius) * (float)j / (float)num3;
float radius = innerRadius + (outerRadius - innerRadius) * (float)(j + 1) / (float)num3;
if (num7 > 0f)
{
Vector3 vector3 = TerrainVertex(origin, direction, num7, yOffset);
Vector3 vector4 = TerrainVertex(origin, direction, radius, yOffset);
Vector3 vector5 = TerrainVertex(origin, vector2, radius, yOffset);
Vector3 vector6 = TerrainVertex(origin, vector2, num7, yOffset);
DrawTriangle(vector3, vector4, vector5, gradient.ColorAt(vector3).ToVector4(), gradient.ColorAt(vector4).ToVector4(), gradient.ColorAt(vector5).ToVector4(), p);
DrawTriangle(vector5, vector6, vector3, gradient.ColorAt(vector5).ToVector4(), gradient.ColorAt(vector6).ToVector4(), gradient.ColorAt(vector3).ToVector4(), p);
}
else
{
Vector3 vector7 = TerrainVertex(origin, direction, radius, yOffset);
Vector3 vector8 = TerrainVertex(origin, vector2, radius, yOffset);
DrawTriangle(vector, vector7, vector8, colorA, gradient.ColorAt(vector7).ToVector4(), gradient.ColorAt(vector8).ToVector4(), p);
}
}
direction = vector2;
}
}
internal void DrawTerrainQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d, uint colorA, uint colorB, uint colorC, uint colorD, float resolution, float yOffset, AxDxParams p = default(AxDxParams))
{
float num = MathF.Max(Vector3.Distance(a, b), Vector3.Distance(d, c));
float num2 = MathF.Max(Vector3.Distance(b, c), Vector3.Distance(a, d));
int num3 = Math.Max(1, (int)MathF.Ceiling(num / resolution));
int num4 = Math.Max(1, (int)MathF.Ceiling(num2 / resolution));
Vector4 a2 = colorA.ToVector4();
Vector4 b2 = colorB.ToVector4();
Vector4 c2 = colorC.ToVector4();
Vector4 d2 = colorD.ToVector4();
for (int i = 0; i < num3; i++)
{
float u = (float)i / (float)num3;
float u2 = (float)(i + 1) / (float)num3;
for (int j = 0; j < num4; j++)
{
float v = (float)j / (float)num4;
float v2 = (float)(j + 1) / (float)num4;
Vector3 vector = TerrainVertexBilinear(a, b, c, d, u, v, yOffset);
Vector3 b3 = TerrainVertexBilinear(a, b, c, d, u2, v, yOffset);
Vector3 vector2 = TerrainVertexBilinear(a, b, c, d, u2, v2, yOffset);
Vector3 b4 = TerrainVertexBilinear(a, b, c, d, u, v2, yOffset);
Vector4 vector3 = BilinearColorV4(a2, b2, c2, d2, u, v);
Vector4 colorB2 = BilinearColorV4(a2, b2, c2, d2, u2, v);
Vector4 vector4 = BilinearColorV4(a2, b2, c2, d2, u2, v2);
Vector4 colorB3 = BilinearColorV4(a2, b2, c2, d2, u, v2);
DrawTriangle(vector, b3, vector2, vector3, colorB2, vector4, p);
DrawTriangle(vector2, b4, vector, vector4, colorB3, vector3, p);
}
}
}
internal void DrawTerrainQuadGradient<T>(Vector3 a, Vector3 b, Vector3 c, Vector3 d, T gradient, float resolution, float yOffset, AxDxParams p = default(AxDxParams)) where T : IPctGradient
{
float num = MathF.Max(Vector3.Distance(a, b), Vector3.Distance(d, c));
float num2 = MathF.Max(Vector3.Distance(b, c), Vector3.Distance(a, d));
int num3 = Math.Max(1, (int)MathF.Ceiling(num / resolution));
int num4 = Math.Max(1, (int)MathF.Ceiling(num2 / resolution));
for (int i = 0; i < num3; i++)
{
float u = (float)i / (float)num3;
float u2 = (float)(i + 1) / (float)num3;
for (int j = 0; j < num4; j++)
{
float v = (float)j / (float)num4;
float v2 = (float)(j + 1) / (float)num4;
Vector3 vector = TerrainVertexBilinear(a, b, c, d, u, v, yOffset);
Vector3 vector2 = TerrainVertexBilinear(a, b, c, d, u2, v, yOffset);
Vector3 vector3 = TerrainVertexBilinear(a, b, c, d, u2, v2, yOffset);
Vector3 vector4 = TerrainVertexBilinear(a, b, c, d, u, v2, yOffset);
DrawTriangle(vector, vector2, vector3, gradient.ColorAt(vector).ToVector4(), gradient.ColorAt(vector2).ToVector4(), gradient.ColorAt(vector3).ToVector4(), p);
DrawTriangle(vector3, vector4, vector, gradient.ColorAt(vector3).ToVector4(), gradient.ColorAt(vector4).ToVector4(), gradient.ColorAt(vector).ToVector4(), p);
}
}
}
private static Vector3 TerrainVertex(Vector3 origin, Vector3 direction, float radius, float yOffset)
{
float x = origin.X + direction.X * radius;
float z = origin.Z + direction.Z * radius;
float terrainYRaw = AxTerrainQuery.GetTerrainYRaw(x, z);
float y = (float.IsNaN(terrainYRaw) ? origin.Y : terrainYRaw) + yOffset;
return new Vector3(x, y, z);
}
private static Vector3 TerrainVertexBilinear(Vector3 a, Vector3 b, Vector3 c, Vector3 d, float u, float v, float yOffset)
{
Vector3 result = a * (1f - u) * (1f - v) + b * u * (1f - v) + c * u * v + d * (1f - u) * v;
float terrainYRaw = AxTerrainQuery.GetTerrainYRaw(result.X, result.Z);
float y = result.Y;
result.Y = (float.IsNaN(terrainYRaw) ? y : terrainYRaw) + yOffset;
return result;
}
private static Vector4 BilinearColorV4(Vector4 a, Vector4 b, Vector4 c, Vector4 d, float u, float v)
{
return a * (1f - u) * (1f - v) + b * u * (1f - v) + c * u * v + d * (1f - u) * v;
}
public void DrawFan(Vector3 center, float innerRadius, float outerRadius, float minAngle, float maxAngle, uint innerColor, uint outerColor, uint numSegments = 0u, AxDxParams p = default(AxDxParams))
{
if (!FanDegraded)
{
if (numSegments == 0)
{
numSegments = (uint)MathF.Max(4f, MathF.Ceiling(MathF.Abs(maxAngle - minAngle) * 16f));
}
numSegments = Math.Min(numSegments, 360u);
GetFanFills().Add(center, innerRadius, outerRadius, minAngle, maxAngle, innerColor.ToVector4(), outerColor.ToVector4(), numSegments, p);
}
else
{
DrawTriangleFan(center, innerRadius, outerRadius, minAngle, maxAngle, innerColor, outerColor, numSegments, p);
}
}
private FanFillShader.Data.Builder GetFanFills()
{
return _fanFillDynamicBuilder ?? (_fanFillDynamicBuilder = _fanFillDynamicData.Map(FrameRenderContext));
}
public void DrawStroke(List<Vector3> world, float thickness, uint color, bool closed = false, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, AxLineCap cap = AxLineCap.Butt, AxLineJoin join = AxLineJoin.None, AxDxParams p = default(AxDxParams))
{
if (!StrokeDegraded)
{
GetStroke().Add(world, thickness, color.ToVector4(), closed, dashLength, gapLength, dashOffset, colorEnd.HasValue ? new Vector4?(colorEnd.Value.ToVector4()) : ((Vector4?)null), (float)cap, (float)join, p);
}
}
public void DrawStroke(List<Vector3> world, ReadOnlySpan<float> thicknesses, uint color, bool closed = false, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, AxLineCap cap = AxLineCap.Butt, AxLineJoin join = AxLineJoin.None, AxDxParams p = default(AxDxParams))
{
if (!StrokeDegraded)
{
GetStroke().Add(world, thicknesses, color.ToVector4(), closed, dashLength, gapLength, dashOffset, colorEnd.HasValue ? new Vector4?(colorEnd.Value.ToVector4()) : ((Vector4?)null), (float)cap, (float)join, p);
}
}
private StrokeShader.Data.Builder GetStroke()
{
return _strokeDynamicBuilder ?? (_strokeDynamicBuilder = _strokeDynamicData.Map(FrameRenderContext));
}
public void DrawTexturedQuad(IntPtr textureSRV, Vector3 a, Vector3 b, Vector3 c, Vector3 d, uint color)
{
DrawTexturedQuad(textureSRV, a, b, c, d, color, new Vector2(0f, 0f), new Vector2(1f, 1f));
}
public void DrawTexturedQuad(IntPtr textureSRV, Vector3 a, Vector3 b, Vector3 c, Vector3 d, uint color, Vector2 uvMin, Vector2 uvMax)
{
QuadBlitter.Data.Builder texturedQuads = GetTexturedQuads();
texturedQuads.SetTexture(textureSRV);
Vector4 color2 = color.ToVector4();
texturedQuads.Add(a, new Vector2(uvMin.X, uvMin.Y), color2);
texturedQuads.Add(b, new Vector2(uvMax.X, uvMin.Y), color2);
texturedQuads.Add(c, new Vector2(uvMax.X, uvMax.Y), color2);
texturedQuads.Add(a, new Vector2(uvMin.X, uvMin.Y), color2);
texturedQuads.Add(c, new Vector2(uvMax.X, uvMax.Y), color2);
texturedQuads.Add(d, new Vector2(uvMin.X, uvMax.Y), color2);
}
private QuadBlitter.Data.Builder GetTexturedQuads()
{
return _texturedQuadDynamicBuilder ?? (_texturedQuadDynamicBuilder = _texturedQuadDynamicData.Map(FrameRenderContext));
}
}

View file

@ -0,0 +1,288 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class FanFillShader : IDisposable
{
public struct Constants
{
public Matrix4x4 ViewProj;
public Vector2 PixelToUv;
}
public struct Instance
{
public Vector3 Origin;
public float InnerRadius;
public float OuterRadius;
public float MinAngle;
public float MaxAngle;
public float NumSegments;
public Vector4 ColorOrigin;
public Vector4 ColorEnd;
public float OccludedAlpha;
public float OcclusionTolerance;
public float FadeStart;
public float FadeStop;
}
public sealed class Data : HlslShaderData<Instance>
{
public sealed class Builder : HlslShaderBuilder<Instance>
{
internal Builder(FrameRenderContext ctx, Data data)
: base(data._buffer.Map(ctx))
{
}
public void Add(Vector3 world, float innerRadius, float outerRadius, float minAngle, float maxAngle, Vector4 colorOrigin, Vector4 colorEnd, float numSegments, AxDxParams p)
{
_inner.Add(new Instance
{
Origin = world,
InnerRadius = innerRadius,
OuterRadius = outerRadius,
MinAngle = minAngle,
MaxAngle = maxAngle,
NumSegments = numSegments,
ColorOrigin = colorOrigin,
ColorEnd = colorEnd,
OccludedAlpha = p.OccludedAlpha,
OcclusionTolerance = p.OcclusionTolerance,
FadeStart = p.FadeStart,
FadeStop = p.FadeStop
});
}
}
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
: base("Fan", ctx, maxCount, dynamic)
{
}
public Builder Map(FrameRenderContext ctx)
{
return new Builder(ctx, this);
}
public unsafe void DrawAll(FrameRenderContext ctx)
{
ID3D11Buffer* buffer = _buffer.Buffer;
uint elementSize = (uint)_buffer.ElementSize;
uint num = 0u;
ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num);
ctx.Context->DrawInstanced(722u, (uint)_buffer.CurElements, 0u, 0u);
}
}
private const int VerticesPerInstance = 722;
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11InputLayout* _il;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
public unsafe FanFillShader(FrameRenderContext ctx)
{
byte[] bytes = Encoding.UTF8.GetBytes("#define PI 3.14159265359f\n#define MAX_SEGMENTS 360\n\ncbuffer Constants : register(b0)\n{\n float4x4 viewProj;\n float2 pixelToUv;\n};\n\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}\n\nstruct Fan\n{\n float3 origin : WORLD;\n float innerRadius : RADIUS0;\n float outerRadius : RADIUS1;\n float minAngle : ANGLE0;\n float maxAngle : ANGLE1;\n float numSegments : NUMSEGMENTS;\n float4 colorOrigin : INSTANCECOLOR0;\n float4 colorEnd : INSTANCECOLOR1;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float4 color : COLOR;\n float2 tex : TEXCOORD;\n float4 fadeParams : FADEPARAMS;\n};\n\nVSOutput vs(in Fan instance, uint vertexId: SV_VERTEXID, uint instanceId: SV_INSTANCEID)\n{\n VSOutput o;\n\n uint segs = (uint)instance.numSegments;\n uint i = min(vertexId / 2, segs);\n\n float radius = 0;\n if (vertexId % 2 == 0) {\n o.color = instance.colorOrigin;\n o.tex.y = 0;\n radius = instance.innerRadius;\n } else {\n o.color = instance.colorEnd;\n o.tex.y = instance.outerRadius - instance.innerRadius;\n radius = instance.outerRadius;\n }\n float totalAngle = instance.maxAngle - instance.minAngle;\n float angleStep = totalAngle / instance.numSegments;\n float angle = PI / 2 + instance.minAngle + i * angleStep;\n float3 offset = radius * float3(cos(angle), 0, sin(angle));\n\n o.tex.x = angle;\n o.projPos = mul(float4(instance.origin + offset, 1.0), viewProj);\n o.fadeParams = instance.fadeParams;\n return o;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return applyShared(input.color, input.projPos.xyz, input.fadeParams);\n}");
TriangleFillShader.CompileShader(bytes, "vs"u8, "vs_5_0"u8, out var blob, "Circle VS");
TriangleFillShader.CompileShader(bytes, "ps"u8, "ps_5_0"u8, out var blob2, "Circle PS");
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
_vs = vs;
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
_ps = ps;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = 80u,
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 4u
};
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
_constantBuffer = constantBuffer;
fixed (byte* semanticName = "WORLD"u8)
{
fixed (byte* semanticName2 = "RADIUS"u8)
{
fixed (byte* semanticName3 = "ANGLE"u8)
{
fixed (byte* semanticName4 = "NUMSEGMENTS"u8)
{
fixed (byte* semanticName5 = "INSTANCECOLOR"u8)
{
fixed (byte* semanticName6 = "FADEPARAMS"u8)
{
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[9];
*ptr = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName2,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[2] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName2,
SemanticIndex = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[3] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName3,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[4] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName3,
SemanticIndex = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[5] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName4,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[6] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName5,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[7] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName5,
SemanticIndex = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ptr[8] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName6,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
InstanceDataStepRate = 1u
};
ID3D11InputLayout* il = default(ID3D11InputLayout*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 9u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
_il = il;
}
}
}
}
}
}
blob->Release();
blob2->Release();
}
public unsafe void Dispose()
{
if (_constantBuffer != null)
{
_constantBuffer->Release();
_constantBuffer = null;
}
if (_il != null)
{
_il->Release();
_il = null;
}
if (_vs != null)
{
_vs->Release();
_vs = null;
}
if (_ps != null)
{
_ps->Release();
_ps = null;
}
GC.SuppressFinalize(this);
}
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
{
consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj);
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
}
public unsafe void Bind(FrameRenderContext ctx)
{
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ctx.Context->IASetInputLayout(_il);
ctx.Context->VSSetShader(_vs, null, 0u);
ID3D11Buffer* constantBuffer = _constantBuffer;
ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->PSSetShader(_ps, null, 0u);
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->GSSetShader(null, null, 0u);
}
public void Draw(FrameRenderContext ctx, Data data)
{
Bind(ctx);
data.DrawAll(ctx);
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class FrameRenderContext : IDisposable
{
public unsafe ID3D11Device* Device { get; private set; }
public unsafe ID3D11DeviceContext* Context { get; private set; }
public unsafe FrameRenderContext()
{
Device = (ID3D11Device*)FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device.Instance()->D3D11Forwarder;
Device->AddRef();
ID3D11DeviceContext* context = default(ID3D11DeviceContext*);
Marshal.ThrowExceptionForHR(Device->CreateDeferredContext(0u, &context));
Context = context;
}
public unsafe void Dispose()
{
if (Context != null)
{
Context->Release();
Context = null;
}
if (Device != null)
{
Device->Release();
Device = null;
}
GC.SuppressFinalize(this);
}
public unsafe void Execute()
{
ID3D11CommandList* ptr = default(ID3D11CommandList*);
Marshal.ThrowExceptionForHR(Context->FinishCommandList(false, &ptr));
ID3D11DeviceContext* ptr2 = default(ID3D11DeviceContext*);
Device->GetImmediateContext(&ptr2);
ptr2->ExecuteCommandList(ptr, true);
ptr2->Release();
ptr->Release();
Context->ClearState();
}
}

View file

@ -0,0 +1,293 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Auspex.Rendering.Direct3D;
internal sealed class FrameRenderTarget : IDisposable
{
private unsafe ID3D11Texture2D* _baseRT;
private unsafe ID3D11RenderTargetView* _baseRTV;
private unsafe ID3D11ShaderResourceView* _baseSRV;
private unsafe ID3D11Texture2D* _processedRT;
private unsafe ID3D11RenderTargetView* _processedRTV;
private unsafe ID3D11ShaderResourceView* _processedSRV;
private unsafe ID3D11Texture2D* _backBufferCopy;
private unsafe ID3D11ShaderResourceView* _backBufferSRV;
private unsafe ID3D11Texture2D* _clipStencil;
private unsafe ID3D11DepthStencilView* _clipStencilDSV;
private unsafe ID3D11BlendState* _defaultBlendState;
private unsafe ID3D11BlendState* _fspBlendState;
public Vector2 Size { get; private set; }
public uint Width => (uint)Size.X;
public uint Height => (uint)Size.Y;
internal unsafe ID3D11DepthStencilView* ClipStencilDSV => _clipStencilDSV;
internal unsafe ID3D11RenderTargetView* BaseRTV => _baseRTV;
public unsafe IntPtr ImguiHandle => (nint)_processedSRV;
public AxTexture Texture => new AxTexture(ImguiHandle, Width, Height);
public unsafe FrameRenderTarget(FrameRenderContext ctx, int width, int height, AlphaBlendMode blendMode)
{
Size = new Vector2(width, height);
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = new D3D11_TEXTURE2D_DESC
{
Width = (uint)width,
Height = (uint)height,
MipLevels = 1u,
ArraySize = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc = new DXGI_SAMPLE_DESC
{
Count = 1u,
Quality = 0u
},
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 40u,
CPUAccessFlags = 0u,
MiscFlags = 0u
};
ID3D11Texture2D* ptr = default(ID3D11Texture2D*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateTexture2D(&d3D11_TEXTURE2D_DESC, null, &ptr));
_baseRT = ptr;
ID3D11RenderTargetView* ptr2 = default(ID3D11RenderTargetView*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateRenderTargetView((ID3D11Resource*)_baseRT, null, &ptr2));
_baseRTV = ptr2;
ID3D11ShaderResourceView* ptr3 = default(ID3D11ShaderResourceView*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateShaderResourceView((ID3D11Resource*)_baseRT, null, &ptr3));
_baseSRV = ptr3;
Marshal.ThrowExceptionForHR(ctx.Device->CreateTexture2D(&d3D11_TEXTURE2D_DESC, null, &ptr));
_processedRT = ptr;
Marshal.ThrowExceptionForHR(ctx.Device->CreateRenderTargetView((ID3D11Resource*)_processedRT, null, &ptr2));
_processedRTV = ptr2;
Marshal.ThrowExceptionForHR(ctx.Device->CreateShaderResourceView((ID3D11Resource*)_processedRT, null, &ptr3));
_processedSRV = ptr3;
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC2 = new D3D11_TEXTURE2D_DESC
{
Width = (uint)width,
Height = (uint)height,
MipLevels = 1u,
ArraySize = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS,
SampleDesc = new DXGI_SAMPLE_DESC
{
Count = 1u,
Quality = 0u
},
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 64u,
CPUAccessFlags = 0u,
MiscFlags = 0u
};
Marshal.ThrowExceptionForHR(ctx.Device->CreateTexture2D(&d3D11_TEXTURE2D_DESC2, null, &ptr));
_clipStencil = ptr;
D3D11_DEPTH_STENCIL_VIEW_DESC d3D11_DEPTH_STENCIL_VIEW_DESC = new D3D11_DEPTH_STENCIL_VIEW_DESC
{
Format = DXGI_FORMAT.DXGI_FORMAT_D24_UNORM_S8_UINT,
ViewDimension = D3D11_DSV_DIMENSION.D3D11_DSV_DIMENSION_TEXTURE2D
};
d3D11_DEPTH_STENCIL_VIEW_DESC.Texture2D.MipSlice = 0u;
ID3D11DepthStencilView* clipStencilDSV = default(ID3D11DepthStencilView*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateDepthStencilView((ID3D11Resource*)_clipStencil, &d3D11_DEPTH_STENCIL_VIEW_DESC, &clipStencilDSV));
_clipStencilDSV = clipStencilDSV;
D3D11_BLEND_DESC d3D11_BLEND_DESC = default(D3D11_BLEND_DESC);
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).RenderTargetWriteMask = 15;
if (blendMode != AlphaBlendMode.None)
{
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).BlendEnable = true;
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).SrcBlend = D3D11_BLEND.D3D11_BLEND_SRC_ALPHA;
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).DestBlend = D3D11_BLEND.D3D11_BLEND_INV_SRC_ALPHA;
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).BlendOp = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD;
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).SrcBlendAlpha = D3D11_BLEND.D3D11_BLEND_ONE;
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).DestBlendAlpha = D3D11_BLEND.D3D11_BLEND_INV_SRC_ALPHA;
switch (blendMode)
{
case AlphaBlendMode.Add:
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).BlendOpAlpha = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD;
break;
case AlphaBlendMode.Max:
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).BlendOpAlpha = D3D11_BLEND_OP.D3D11_BLEND_OP_MAX;
break;
}
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC.RenderTarget).RenderTargetWriteMask = 15;
}
ID3D11BlendState* ptr4 = default(ID3D11BlendState*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBlendState(&d3D11_BLEND_DESC, &ptr4));
_defaultBlendState = ptr4;
D3D11_BLEND_DESC d3D11_BLEND_DESC2 = default(D3D11_BLEND_DESC);
global::_003CPrivateImplementationDetails_003E.InlineArrayFirstElementRef<D3D11_BLEND_DESC._RenderTarget_e__FixedBuffer, D3D11_RENDER_TARGET_BLEND_DESC>(ref d3D11_BLEND_DESC2.RenderTarget).RenderTargetWriteMask = 15;
Marshal.ThrowExceptionForHR(ctx.Device->CreateBlendState(&d3D11_BLEND_DESC2, &ptr4));
_fspBlendState = ptr4;
}
public unsafe void Bind(FrameRenderContext ctx)
{
byte* intPtr = stackalloc byte[16];
// IL initblk instruction
Unsafe.InitBlock(intPtr, 0, 16);
float* colorRGBA = (float*)intPtr;
ctx.Context->ClearRenderTargetView(_baseRTV, colorRGBA);
D3D11_VIEWPORT d3D11_VIEWPORT = new D3D11_VIEWPORT
{
TopLeftX = 0f,
TopLeftY = 0f,
Width = Size.X,
Height = Size.Y,
MinDepth = 0f,
MaxDepth = 1f
};
ctx.Context->RSSetViewports(1u, &d3D11_VIEWPORT);
ctx.Context->OMSetBlendState(_defaultBlendState, null, uint.MaxValue);
ID3D11RenderTargetView* baseRTV = _baseRTV;
ctx.Context->OMSetRenderTargets(1u, &baseRTV, null);
}
public unsafe void CopyBaseToProcessed(FrameRenderContext ctx)
{
ctx.Context->CopyResource((ID3D11Resource*)_processedRT, (ID3D11Resource*)_baseRT);
}
public unsafe void ExecuteFSP(FrameRenderContext ctx, ID3D11Texture2D* backBuffer, FullscreenPassShader fsp, bool clipNativeUI)
{
if (clipNativeUI)
{
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = default(D3D11_TEXTURE2D_DESC);
backBuffer->GetDesc(&d3D11_TEXTURE2D_DESC);
ValidateBackBufferResources(ctx.Device, &d3D11_TEXTURE2D_DESC);
ctx.Context->CopyResource((ID3D11Resource*)_backBufferCopy, (ID3D11Resource*)backBuffer);
}
ctx.Context->OMSetBlendState(_fspBlendState, null, uint.MaxValue);
byte* intPtr = stackalloc byte[16];
// IL initblk instruction
Unsafe.InitBlock(intPtr, 0, 16);
float* colorRGBA = (float*)intPtr;
ctx.Context->ClearRenderTargetView(_processedRTV, colorRGBA);
ID3D11RenderTargetView* processedRTV = _processedRTV;
ctx.Context->OMSetRenderTargets(1u, &processedRTV, null);
fsp.Draw(ctx, _baseSRV, _backBufferSRV);
}
private unsafe void ValidateBackBufferResources(ID3D11Device* device, D3D11_TEXTURE2D_DESC* backBufferDesc)
{
bool flag = _backBufferCopy == null;
if (!flag)
{
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = default(D3D11_TEXTURE2D_DESC);
_backBufferCopy->GetDesc(&d3D11_TEXTURE2D_DESC);
flag = d3D11_TEXTURE2D_DESC.Width != backBufferDesc->Width || d3D11_TEXTURE2D_DESC.Height != backBufferDesc->Height;
}
if (flag)
{
if (_backBufferCopy != null)
{
_backBufferCopy->Release();
_backBufferCopy = null;
}
if (_backBufferSRV != null)
{
_backBufferSRV->Release();
_backBufferSRV = null;
}
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC2 = *backBufferDesc;
d3D11_TEXTURE2D_DESC2.BindFlags = 8u;
ID3D11Texture2D* backBufferCopy = default(ID3D11Texture2D*);
HRESULT hRESULT = device->CreateTexture2D(&d3D11_TEXTURE2D_DESC2, null, &backBufferCopy);
Marshal.ThrowExceptionForHR(hRESULT);
_backBufferCopy = backBufferCopy;
ID3D11ShaderResourceView* backBufferSRV = default(ID3D11ShaderResourceView*);
hRESULT = device->CreateShaderResourceView((ID3D11Resource*)_backBufferCopy, null, &backBufferSRV);
if (hRESULT < 0)
{
_backBufferCopy->Release();
_backBufferCopy = null;
Marshal.ThrowExceptionForHR(hRESULT);
}
_backBufferSRV = backBufferSRV;
}
}
public unsafe void Dispose()
{
GC.SuppressFinalize(this);
if (_baseRT != null)
{
_baseRT->Release();
_baseRT = null;
}
if (_baseRTV != null)
{
_baseRTV->Release();
_baseRTV = null;
}
if (_baseSRV != null)
{
_baseSRV->Release();
_baseSRV = null;
}
if (_processedRT != null)
{
_processedRT->Release();
_processedRT = null;
}
if (_processedRTV != null)
{
_processedRTV->Release();
_processedRTV = null;
}
if (_processedSRV != null)
{
_processedSRV->Release();
_processedSRV = null;
}
if (_backBufferCopy != null)
{
_backBufferCopy->Release();
_backBufferCopy = null;
}
if (_backBufferSRV != null)
{
_backBufferSRV->Release();
_backBufferSRV = null;
}
if (_clipStencilDSV != null)
{
_clipStencilDSV->Release();
_clipStencilDSV = null;
}
if (_clipStencil != null)
{
_clipStencil->Release();
_clipStencil = null;
}
if (_defaultBlendState != null)
{
_defaultBlendState->Release();
_defaultBlendState = null;
}
if (_fspBlendState != null)
{
_fspBlendState->Release();
_fspBlendState = null;
}
}
}

View file

@ -0,0 +1,91 @@
using System;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class FullscreenPassShader : IDisposable
{
public struct Constants
{
public float MaxAlpha;
public float ClipNativeUI;
}
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
public unsafe FullscreenPassShader(FrameRenderContext ctx)
{
ReadOnlySpan<byte> source = "struct Constants\n{\n float maxAlpha;\n float clipNativeUI;\n};\nConstants k : register(b0);\n\nTexture2D<float4> inputTexture : register(t0);\nTexture2D<float4> maskTexture : register(t1);\n\nSamplerState TextureSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\nstruct VSOutput\n{\n float4 pos : SV_POSITION;\n float2 uv: TEXCOORD;\n};\n\nVSOutput vs(uint id : SV_VertexID)\n{\n VSOutput output;\n\tfloat2 uv = float2((id << 1) & 2, id & 2);\n\toutput.pos = float4(uv * float2(2, -2) + float2(-1, 1), 0, 1);\n output.uv = uv;\n return output;\n}\n\nfloat4 ps(VSOutput input) : SV_Target\n{\n float4 color = inputTexture.Sample(TextureSampler, input.uv);\n if (color.a > 0)\n {\n color.rgb /= color.a;\n }\n float maskAlpha = 1;\n if (k.clipNativeUI > 0.5)\n {\n float4 mask = maskTexture.Sample(TextureSampler, input.uv);\n // Apply mask alpha squared\n // (I don't think this is mathematically correct but it looks better)\n maskAlpha = 1 - mask.a;\n maskAlpha *= maskAlpha;\n }\n color.a = min(color.a, k.maxAlpha) * maskAlpha;\n return color;\n}"u8;
TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "FSP VS");
TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "FSP PS");
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
_vs = vs;
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
_ps = ps;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = 16u,
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 4u
};
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
_constantBuffer = constantBuffer;
blob->Release();
blob2->Release();
}
public unsafe void Dispose()
{
if (_constantBuffer != null)
{
_constantBuffer->Release();
_constantBuffer = null;
}
if (_vs != null)
{
_vs->Release();
_vs = null;
}
if (_ps != null)
{
_ps->Release();
_ps = null;
}
GC.SuppressFinalize(this);
}
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
{
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
}
public unsafe void Bind(FrameRenderContext ctx)
{
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx.Context->VSSetShader(_vs, null, 0u);
ctx.Context->PSSetShader(_ps, null, 0u);
ID3D11Buffer* constantBuffer = _constantBuffer;
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->GSSetShader(null, null, 0u);
}
public unsafe void Draw(FrameRenderContext ctx, ID3D11ShaderResourceView* baseSRV, ID3D11ShaderResourceView* maskSRV)
{
ctx.Context->PSSetShaderResources(0u, 1u, &baseSRV);
ctx.Context->PSSetShaderResources(1u, 1u, &maskSRV);
Bind(ctx);
ctx.Context->Draw(3u, 0u);
ID3D11ShaderResourceView* ptr = null;
ctx.Context->PSSetShaderResources(0u, 1u, &ptr);
ctx.Context->PSSetShaderResources(1u, 1u, &ptr);
}
}

View file

@ -0,0 +1,147 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Plugin.Services;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class GpuBuffer<T> : IDisposable where T : unmanaged
{
public sealed class Builder : IDisposable
{
private FrameRenderContext _ctx;
private GpuBuffer<T> _buffer;
private unsafe byte* _dataPtr;
private int _offset;
private unsafe ID3D11Buffer* _staging;
public int CurElements => _buffer.CurElements;
internal unsafe Builder(FrameRenderContext ctx, GpuBuffer<T> buffer)
{
_ctx = ctx;
_buffer = buffer;
buffer.CurElements = 0;
buffer._hasWarnedOverflow = false;
_offset = 0;
D3D11_MAPPED_SUBRESOURCE d3D11_MAPPED_SUBRESOURCE = default(D3D11_MAPPED_SUBRESOURCE);
if (buffer.Dynamic)
{
Marshal.ThrowExceptionForHR(ctx.Context->Map((ID3D11Resource*)buffer.Buffer, 0u, D3D11_MAP.D3D11_MAP_WRITE_DISCARD, 0u, &d3D11_MAPPED_SUBRESOURCE));
_dataPtr = (byte*)d3D11_MAPPED_SUBRESOURCE.pData;
return;
}
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = (uint)(buffer.ElementSize * buffer.MaxElements),
Usage = D3D11_USAGE.D3D11_USAGE_STAGING,
CPUAccessFlags = 65536u
};
ID3D11Buffer* staging = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &staging));
_staging = staging;
Marshal.ThrowExceptionForHR(ctx.Context->Map((ID3D11Resource*)_staging, 0u, D3D11_MAP.D3D11_MAP_WRITE_DISCARD, 0u, &d3D11_MAPPED_SUBRESOURCE));
_dataPtr = (byte*)d3D11_MAPPED_SUBRESOURCE.pData;
}
public unsafe void Dispose()
{
if (_buffer.Dynamic)
{
_ctx.Context->Unmap((ID3D11Resource*)_buffer.Buffer, 0u);
}
else
{
_ctx.Context->Unmap((ID3D11Resource*)_staging, 0u);
_ctx.Context->CopyResource((ID3D11Resource*)_buffer.Buffer, (ID3D11Resource*)_staging);
_staging->Release();
_staging = null;
}
GC.SuppressFinalize(this);
}
public unsafe void Add(ref T item)
{
if (_buffer.CurElements >= _buffer.MaxElements)
{
if (!_buffer._hasWarnedOverflow)
{
_buffer._hasWarnedOverflow = true;
IPluginLog log = AuspexService.Log;
DefaultInterpolatedStringHandler val = default(DefaultInterpolatedStringHandler);
((DefaultInterpolatedStringHandler)(ref val))._002Ector(60, 2);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral("[Auspex] ");
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted(_buffer.FriendlyName);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral(" buffer full (");
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted<int>(_buffer.MaxElements);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral(" elements); further elements dropped.");
log.Warning(((DefaultInterpolatedStringHandler)(ref val)).ToStringAndClear());
}
}
else
{
GpuBuffer<T> buffer = _buffer;
int curElements = buffer.CurElements + 1;
buffer.CurElements = curElements;
Unsafe.CopyBlockUnaligned((void*)(_dataPtr + _offset), Unsafe.AsPointer<T>(ref item), (uint)sizeof(T));
_offset += sizeof(T);
}
}
public void Add(T item)
{
Add(ref item);
}
}
private bool _hasWarnedOverflow;
public string FriendlyName { get; private set; }
public bool Dynamic { get; init; }
public int ElementSize { get; init; }
public int MaxElements { get; init; }
public int CurElements { get; private set; }
public unsafe ID3D11Buffer* Buffer { get; init; }
public unsafe GpuBuffer(string friendlyName, FrameRenderContext ctx, int maxElements, uint bindFlags, bool dynamic)
{
FriendlyName = friendlyName;
Dynamic = dynamic;
ElementSize = sizeof(T);
MaxElements = maxElements;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = (uint)(ElementSize * maxElements),
Usage = (dynamic ? D3D11_USAGE.D3D11_USAGE_DYNAMIC : D3D11_USAGE.D3D11_USAGE_DEFAULT),
BindFlags = bindFlags,
CPUAccessFlags = (dynamic ? 65536u : 0u)
};
ID3D11Buffer* buffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &buffer));
Buffer = buffer;
}
public unsafe void Dispose()
{
if (Buffer != null)
{
Buffer->Release();
}
GC.SuppressFinalize(this);
}
public Builder Map(FrameRenderContext ctx)
{
return new Builder(ctx, this);
}
}

View file

@ -0,0 +1,146 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Bindings.ImGui;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class HighResFontAtlas : IDisposable
{
private struct ImFontConfigHead
{
public IntPtr FontData;
public int FontDataSize;
public byte FontDataOwnedByAtlas;
private byte _pad1;
private byte _pad2;
private byte _pad3;
public int FontNo;
public float SizePixels;
}
private const float HighResFontSize = 64f;
private IntPtr _atlas;
private unsafe ID3D11Texture2D* _texture;
private unsafe ID3D11ShaderResourceView* _srv;
public ImFontPtr Font { get; }
public unsafe IntPtr TextureSRV => (nint)_srv;
public unsafe HighResFontAtlas(FrameRenderContext ctx)
{
_atlas = ImFontAtlas_ImFontAtlas();
try
{
IntPtr intPtr = ImFontConfig_ImFontConfig();
try
{
((ImFontConfigHead*)intPtr)->SizePixels = 64f;
IntPtr intPtr2 = ImFontAtlas_AddFontDefault(_atlas, intPtr);
Font = Unsafe.As<IntPtr, ImFontPtr>(ref intPtr2);
}
finally
{
ImFontConfig_destroy(intPtr);
}
if (!ImFontAtlas_Build(_atlas))
{
throw new InvalidOperationException("Failed to build high-res font atlas");
}
byte* pSysMem = default(byte*);
int num = default(int);
int height = default(int);
int num2 = default(int);
ImFontAtlas_GetTexDataAsRGBA32(_atlas, 0, &pSysMem, &num, &height, &num2);
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = new D3D11_TEXTURE2D_DESC
{
Width = (uint)num,
Height = (uint)height,
MipLevels = 1u,
ArraySize = 1u,
Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc = new DXGI_SAMPLE_DESC
{
Count = 1u,
Quality = 0u
},
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 8u
};
D3D11_SUBRESOURCE_DATA d3D11_SUBRESOURCE_DATA = new D3D11_SUBRESOURCE_DATA
{
pSysMem = pSysMem,
SysMemPitch = (uint)(num * num2)
};
ID3D11Texture2D* texture = default(ID3D11Texture2D*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateTexture2D(&d3D11_TEXTURE2D_DESC, &d3D11_SUBRESOURCE_DATA, &texture));
_texture = texture;
ID3D11ShaderResourceView* srv = default(ID3D11ShaderResourceView*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateShaderResourceView((ID3D11Resource*)_texture, null, &srv));
_srv = srv;
ImFontAtlas_ClearTexData(_atlas);
}
catch
{
Dispose();
throw;
}
}
public unsafe void Dispose()
{
if (_srv != null)
{
_srv->Release();
_srv = null;
}
if (_texture != null)
{
_texture->Release();
_texture = null;
}
if (_atlas != (IntPtr)0)
{
ImFontAtlas_destroy(_atlas);
_atlas = (IntPtr)0;
}
GC.SuppressFinalize(this);
}
[DllImport("cimgui")]
private static extern IntPtr ImFontAtlas_ImFontAtlas();
[DllImport("cimgui")]
private static extern void ImFontAtlas_destroy(IntPtr self);
[DllImport("cimgui")]
private static extern IntPtr ImFontAtlas_AddFontDefault(IntPtr self, IntPtr font_cfg);
[DllImport("cimgui")]
[return: MarshalAs(UnmanagedType.U1)]
private static extern bool ImFontAtlas_Build(IntPtr self);
[DllImport("cimgui")]
private unsafe static extern void ImFontAtlas_GetTexDataAsRGBA32(IntPtr self, int texture_index, byte** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel);
[DllImport("cimgui")]
private static extern void ImFontAtlas_ClearTexData(IntPtr self);
[DllImport("cimgui")]
private static extern IntPtr ImFontConfig_ImFontConfig();
[DllImport("cimgui")]
private static extern void ImFontConfig_destroy(IntPtr self);
}

View file

@ -0,0 +1,24 @@
using System;
namespace Auspex.Rendering.Direct3D;
internal abstract class HlslShaderBuilder<TElement> : IDisposable where TElement : unmanaged
{
private protected readonly GpuBuffer<TElement>.Builder _inner;
protected HlslShaderBuilder(GpuBuffer<TElement>.Builder inner)
{
_inner = inner;
}
public virtual void Dispose()
{
_inner.Dispose();
GC.SuppressFinalize(this);
}
public void Add(ref TElement inst)
{
_inner.Add(ref inst);
}
}

View file

@ -0,0 +1,29 @@
using System;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal abstract class HlslShaderData<TElement> : IDisposable where TElement : unmanaged
{
private protected readonly GpuBuffer<TElement> _buffer;
protected HlslShaderData(string name, FrameRenderContext ctx, int maxCount, bool dynamic)
{
_buffer = new GpuBuffer<TElement>(name, ctx, maxCount, 1u, dynamic);
}
public void Dispose()
{
_buffer.Dispose();
GC.SuppressFinalize(this);
}
private protected unsafe void DrawVertices(FrameRenderContext ctx, int first, int count)
{
ID3D11Buffer* buffer = _buffer.Buffer;
uint elementSize = (uint)_buffer.ElementSize;
uint num = 0u;
ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num);
ctx.Context->Draw((uint)count, (uint)first);
}
}

View file

@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class QuadBlitter : IDisposable
{
public struct Constants
{
public Matrix4x4 ViewProj;
}
public struct Vertex
{
public Vector3 Position;
public Vector2 UV;
public Vector4 Color;
}
public struct DrawSegment
{
public IntPtr TextureSRV;
public int StartVertex;
public int VertexCount;
}
public sealed class Data : HlslShaderData<Vertex>
{
public sealed class Builder : HlslShaderBuilder<Vertex>
{
private readonly List<DrawSegment> _segments;
private IntPtr _currentTexture;
private int _segmentStart;
internal Builder(FrameRenderContext ctx, Data data)
: base(data._buffer.Map(ctx))
{
_segments = data._segments;
_segments.Clear();
_currentTexture = (IntPtr)0;
_segmentStart = 0;
}
public override void Dispose()
{
FinalizeSegment();
base.Dispose();
}
public void SetTexture(IntPtr srv)
{
if (srv != _currentTexture)
{
FinalizeSegment();
_currentTexture = srv;
_segmentStart = _inner.CurElements;
}
}
private void FinalizeSegment()
{
int num = _inner.CurElements - _segmentStart;
if (num > 0 && _currentTexture != (IntPtr)0)
{
_segments.Add(new DrawSegment
{
TextureSRV = _currentTexture,
StartVertex = _segmentStart,
VertexCount = num
});
}
}
public void Add(Vector3 position, Vector2 uv, Vector4 color)
{
_inner.Add(new Vertex
{
Position = position,
UV = uv,
Color = color
});
}
}
internal readonly List<DrawSegment> _segments = new List<DrawSegment>();
internal GpuBuffer<Vertex> Buffer => _buffer;
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
: base("QuadBlitter", ctx, maxCount, dynamic)
{
}
public Builder Map(FrameRenderContext ctx)
{
return new Builder(ctx, this);
}
}
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11InputLayout* _il;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
private unsafe ID3D11SamplerState* _sampler;
public unsafe QuadBlitter(FrameRenderContext ctx)
{
ReadOnlySpan<byte> source = "struct Vertex\n{\n float3 pos : POSITION;\n float2 uv : TEXCOORD;\n float4 color : COLOR;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float2 uv : TEXCOORD;\n float4 color : COLOR;\n};\n\nstruct Constants\n{\n float4x4 viewProj;\n};\nConstants k : register(c0);\n\nTexture2D<float4> tex : register(t0);\nSamplerState samp : register(s0);\n\nVSOutput vs(Vertex v)\n{\n VSOutput o;\n o.projPos = mul(float4(v.pos, 1), k.viewProj);\n o.uv = v.uv;\n o.color = v.color;\n return o;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return tex.Sample(samp, input.uv) * input.color;\n}"u8;
TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "QuadBlitter VS");
TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "QuadBlitter PS");
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
_vs = vs;
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
_ps = ps;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = 64u,
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 4u
};
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
_constantBuffer = constantBuffer;
fixed (byte* semanticName = "POSITION"u8)
{
fixed (byte* semanticName2 = "TEXCOORD"u8)
{
fixed (byte* semanticName3 = "COLOR"u8)
{
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[3];
*ptr = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName2,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ptr[2] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName3,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ID3D11InputLayout* il = default(ID3D11InputLayout*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 3u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
_il = il;
}
}
}
D3D11_SAMPLER_DESC d3D11_SAMPLER_DESC = new D3D11_SAMPLER_DESC
{
Filter = D3D11_FILTER.D3D11_FILTER_MIN_MAG_MIP_LINEAR,
AddressU = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP,
AddressV = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP,
AddressW = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP,
MaxAnisotropy = 1u,
ComparisonFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_NEVER,
MaxLOD = float.MaxValue
};
ID3D11SamplerState* sampler = default(ID3D11SamplerState*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateSamplerState(&d3D11_SAMPLER_DESC, &sampler));
_sampler = sampler;
blob->Release();
blob2->Release();
}
public unsafe void Dispose()
{
if (_constantBuffer != null)
{
_constantBuffer->Release();
_constantBuffer = null;
}
if (_il != null)
{
_il->Release();
_il = null;
}
if (_vs != null)
{
_vs->Release();
_vs = null;
}
if (_ps != null)
{
_ps->Release();
_ps = null;
}
if (_sampler != null)
{
_sampler->Release();
_sampler = null;
}
GC.SuppressFinalize(this);
}
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
{
consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj);
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
}
public unsafe void Bind(FrameRenderContext ctx)
{
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx.Context->IASetInputLayout(_il);
ctx.Context->VSSetShader(_vs, null, 0u);
ID3D11Buffer* constantBuffer = _constantBuffer;
ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->PSSetShader(_ps, null, 0u);
ID3D11SamplerState* sampler = _sampler;
ctx.Context->PSSetSamplers(0u, 1u, &sampler);
ctx.Context->GSSetShader(null, null, 0u);
}
public unsafe void Draw(FrameRenderContext ctx, Data data)
{
Bind(ctx);
ID3D11Buffer* buffer = data.Buffer.Buffer;
uint elementSize = (uint)data.Buffer.ElementSize;
uint num = 0u;
ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num);
foreach (DrawSegment segment in data._segments)
{
ID3D11ShaderResourceView* textureSRV = (ID3D11ShaderResourceView*)segment.TextureSRV;
ctx.Context->PSSetShaderResources(0u, 1u, &textureSRV);
ctx.Context->Draw((uint)segment.VertexCount, (uint)segment.StartVertex);
}
ID3D11ShaderResourceView* ptr = null;
ctx.Context->PSSetShaderResources(0u, 1u, &ptr);
}
}

View file

@ -0,0 +1,98 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
using TerraFX.Interop.DirectX;
namespace Auspex.Rendering.Direct3D;
internal sealed class SceneDepthCapture : IDisposable
{
private unsafe ID3D11Texture2D* _copy;
private unsafe ID3D11ShaderResourceView* _copySRV;
private uint _copyWidth;
private uint _copyHeight;
public unsafe ID3D11ShaderResourceView* SRV => _copySRV;
public Vector2 UvScale { get; private set; } = new Vector2(1f, 1f);
public bool IsResolutionScaled { get; private set; }
public unsafe void Dispose()
{
if (_copySRV != null)
{
_copySRV->Release();
_copySRV = null;
}
if (_copy != null)
{
_copy->Release();
_copy = null;
}
GC.SuppressFinalize(this);
}
internal unsafe void Update()
{
RenderTargetManager* ptr = RenderTargetManager.Instance();
Texture* ptr2 = ((ptr != null) ? ptr->DepthStencil : null);
if (ptr2 == null || ptr2->D3D11Texture2D == null)
{
AuspexService.Log.Warning("[Auspex] SceneDepthCapture: scene depth source unavailable.");
return;
}
Device* ptr3 = Device.Instance();
IsResolutionScaled = ptr2->ActualWidth != ptr3->Width || ptr2->ActualHeight != ptr3->Height;
ID3D11Texture2D* d3D11Texture2D = (ID3D11Texture2D*)ptr2->D3D11Texture2D;
D3D11_TEXTURE2D_DESC srcDesc = default(D3D11_TEXTURE2D_DESC);
d3D11Texture2D->GetDesc(&srcDesc);
EnsureCopy(srcDesc);
ID3D11DeviceContext* ptr4 = default(ID3D11DeviceContext*);
((ID3D11Device*)ptr3->D3D11Forwarder)->GetImmediateContext(&ptr4);
ptr4->CopyResource((ID3D11Resource*)_copy, (ID3D11Resource*)d3D11Texture2D);
ptr4->Release();
UvScale = new Vector2((ptr2->AllocatedWidth != 0) ? ((float)ptr2->ActualWidth / (float)ptr2->AllocatedWidth) : 1f, (ptr2->AllocatedHeight != 0) ? ((float)ptr2->ActualHeight / (float)ptr2->AllocatedHeight) : 1f);
}
private unsafe void EnsureCopy(D3D11_TEXTURE2D_DESC srcDesc)
{
if (_copy == null || _copyWidth != srcDesc.Width || _copyHeight != srcDesc.Height)
{
if (_copySRV != null)
{
_copySRV->Release();
_copySRV = null;
}
if (_copy != null)
{
_copy->Release();
_copy = null;
}
ID3D11Device* d3D11Forwarder = (ID3D11Device*)Device.Instance()->D3D11Forwarder;
D3D11_TEXTURE2D_DESC d3D11_TEXTURE2D_DESC = srcDesc;
d3D11_TEXTURE2D_DESC.Format = DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS;
d3D11_TEXTURE2D_DESC.BindFlags = 8u;
ID3D11Texture2D* copy = default(ID3D11Texture2D*);
Marshal.ThrowExceptionForHR(d3D11Forwarder->CreateTexture2D(&d3D11_TEXTURE2D_DESC, null, &copy));
_copy = copy;
_copyWidth = srcDesc.Width;
_copyHeight = srcDesc.Height;
D3D11_SHADER_RESOURCE_VIEW_DESC d3D11_SHADER_RESOURCE_VIEW_DESC = new D3D11_SHADER_RESOURCE_VIEW_DESC
{
Format = DXGI_FORMAT.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
ViewDimension = D3D_SRV_DIMENSION.D3D_SRV_DIMENSION_TEXTURE2D
};
d3D11_SHADER_RESOURCE_VIEW_DESC.Texture2D.MostDetailedMip = 0u;
d3D11_SHADER_RESOURCE_VIEW_DESC.Texture2D.MipLevels = 1u;
ID3D11ShaderResourceView* copySRV = default(ID3D11ShaderResourceView*);
Marshal.ThrowExceptionForHR(d3D11Forwarder->CreateShaderResourceView((ID3D11Resource*)_copy, &d3D11_SHADER_RESOURCE_VIEW_DESC, &copySRV));
_copySRV = copySRV;
}
}
}

View file

@ -0,0 +1,6 @@
namespace Auspex.Rendering.Direct3D;
internal static class ShapeSharedShader
{
public const string Mixin = "\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}";
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,225 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Auspex.Rendering.Direct3D;
internal sealed class TriangleFillShader : IDisposable
{
public struct Constants
{
public Matrix4x4 ViewProj;
public Vector2 PixelToUv;
}
public struct Instance
{
public Vector3 Point;
public Vector4 Color;
public Vector4 FadeParams;
}
public sealed class Data : HlslShaderData<Instance>
{
public sealed class Builder : HlslShaderBuilder<Instance>
{
internal Builder(FrameRenderContext ctx, Data data)
: base(data._buffer.Map(ctx))
{
}
public void Add(Vector3 world, Vector4 color, AxDxParams p)
{
_inner.Add(new Instance
{
Point = world,
Color = color,
FadeParams = new Vector4(p.OccludedAlpha, p.OcclusionTolerance, p.FadeStart, p.FadeStop)
});
}
}
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
: base("Triangle", ctx, maxCount, dynamic)
{
}
public Builder Map(FrameRenderContext ctx)
{
return new Builder(ctx, this);
}
public void DrawSubset(FrameRenderContext ctx, int firstPoint, int numPoints)
{
DrawVertices(ctx, firstPoint, numPoints);
}
public void DrawAll(FrameRenderContext ctx)
{
DrawVertices(ctx, 0, _buffer.CurElements);
}
}
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11InputLayout* _il;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
public unsafe TriangleFillShader(FrameRenderContext ctx)
{
byte[] bytes = Encoding.UTF8.GetBytes("cbuffer Constants : register(b0)\n{\n float4x4 viewProj;\n float2 pixelToUv;\n};\n\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}\n\nstruct Point\n{\n float3 pos : WORLD;\n float4 color : COLOR;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float4 color : COLOR;\n float4 fadeParams : FADEPARAMS;\n};\n\nVSOutput vs(Point v)\n{\n VSOutput vs;\n vs.projPos = mul(float4(v.pos, 1), viewProj);\n vs.color = v.color;\n vs.fadeParams = v.fadeParams;\n return vs;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return applyShared(input.color, input.projPos.xyz, input.fadeParams);\n}");
CompileShader(bytes, "vs"u8, "vs_5_0"u8, out var blob, "Point VS");
CompileShader(bytes, "ps"u8, "ps_5_0"u8, out var blob2, "Point PS");
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
_vs = vs;
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
_ps = ps;
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
{
ByteWidth = 80u,
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = 4u
};
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
_constantBuffer = constantBuffer;
fixed (byte* semanticName = "WORLD"u8)
{
fixed (byte* semanticName2 = "COLOR"u8)
{
fixed (byte* semanticName3 = "FADEPARAMS"u8)
{
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[3];
*ptr = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName2,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ptr[2] = new D3D11_INPUT_ELEMENT_DESC
{
SemanticName = (sbyte*)semanticName3,
SemanticIndex = 0u,
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
AlignedByteOffset = uint.MaxValue,
InputSlot = 0u,
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA,
InstanceDataStepRate = 0u
};
ID3D11InputLayout* il = default(ID3D11InputLayout*);
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 3u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
_il = il;
}
}
}
blob->Release();
blob2->Release();
}
public unsafe void Dispose()
{
if (_constantBuffer != null)
{
_constantBuffer->Release();
_constantBuffer = null;
}
if (_il != null)
{
_il->Release();
_il = null;
}
if (_vs != null)
{
_vs->Release();
_vs = null;
}
if (_ps != null)
{
_ps->Release();
_ps = null;
}
GC.SuppressFinalize(this);
}
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
{
consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj);
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
}
public unsafe void Bind(FrameRenderContext ctx)
{
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx.Context->IASetInputLayout(_il);
ctx.Context->VSSetShader(_vs, null, 0u);
ID3D11Buffer* constantBuffer = _constantBuffer;
ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->PSSetShader(_ps, null, 0u);
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->GSSetShader(null, null, 0u);
}
public void Draw(FrameRenderContext ctx, Data data)
{
Bind(ctx);
data.DrawAll(ctx);
}
internal unsafe static void CompileShader(ReadOnlySpan<byte> source, ReadOnlySpan<byte> entryPoint, ReadOnlySpan<byte> target, out ID3DBlob* blob, string label)
{
ID3DBlob* ptr = null;
ID3DBlob* ptr2 = null;
HRESULT hRESULT;
fixed (byte* pSrcData = source)
{
fixed (byte* pEntrypoint = entryPoint)
{
fixed (byte* pTarget = target)
{
hRESULT = DirectX.D3DCompile(pSrcData, (nuint)source.Length, null, null, null, (sbyte*)pEntrypoint, (sbyte*)pTarget, 0u, 0u, &ptr, &ptr2);
}
}
}
string text = null;
if (ptr2 != null)
{
text = Encoding.UTF8.GetString((byte*)ptr2->GetBufferPointer(), (int)(nuint)ptr2->GetBufferSize()).TrimEnd('\0');
ptr2->Release();
}
AuspexService.Log.Debug(label + " compile: " + text);
if (hRESULT.FAILED)
{
if (ptr != null)
{
ptr->Release();
}
throw new InvalidOperationException("Shader compilation failed (" + label + "): " + text);
}
blob = ptr;
}
}

View file

@ -0,0 +1,64 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using Dalamud.Interface.Utility;
namespace Auspex.Rendering.ImGuiFallback;
internal static class FallbackClipMath
{
public enum LineClipStatus
{
NotVisible,
NotClipped,
A_Clipped,
B_Clipped
}
public static Vector3 XYZ(this Vector4 v)
{
return new Vector3(v.X, v.Y, v.Z);
}
public static LineClipStatus ClipLineToPlane(Vector4 plane, ref Vector3 a, ref Vector3 b, out float t)
{
t = 0f;
float num = Vector4.Dot(new Vector4(a, 1f), plane);
float num2 = Vector4.Dot(new Vector4(b, 1f), plane);
bool flag = num < 0f;
bool flag2 = num2 < 0f;
if (flag && flag2)
{
return LineClipStatus.NotClipped;
}
if (!flag && !flag2)
{
return LineClipStatus.NotVisible;
}
Vector3 vector = b - a;
t = (0f - num) / Vector3.Dot(vector, plane.XYZ());
Vector3 vector2 = a + vector * t;
if (flag)
{
b = vector2;
return LineClipStatus.B_Clipped;
}
a = vector2;
return LineClipStatus.A_Clipped;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void TransformCoordinate(in Vector3 coordinate, in Matrix4x4 transform, out Vector3 result)
{
result.X = coordinate.X * transform.M11 + coordinate.Y * transform.M21 + coordinate.Z * transform.M31 + transform.M41;
result.Y = coordinate.X * transform.M12 + coordinate.Y * transform.M22 + coordinate.Z * transform.M32 + transform.M42;
result.Z = coordinate.X * transform.M13 + coordinate.Y * transform.M23 + coordinate.Z * transform.M33 + transform.M43;
float num = 1f / (coordinate.X * transform.M14 + coordinate.Y * transform.M24 + coordinate.Z * transform.M34 + transform.M44);
result *= num;
}
public static Vector2 WorldToScreenOld(in Matrix4x4 viewProj, in Vector3 worldPos)
{
TransformCoordinate(in worldPos, in viewProj, out var result);
return new Vector2(0.5f * ImGuiHelpers.MainViewport.Size.X * (1f + result.X), 0.5f * ImGuiHelpers.MainViewport.Size.Y * (1f - result.Y)) + ImGuiHelpers.MainViewport.Pos;
}
}

View file

@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
namespace Auspex.Rendering.ImGuiFallback;
internal sealed class ImGuiFallbackRenderer
{
private readonly ImDrawListPtr drawList;
private readonly Matrix4x4 viewProj;
private readonly Vector4 nearPlane;
public unsafe ImGuiFallbackRenderer(ImDrawListPtr drawList)
{
this.drawList = drawList;
viewProj = Control.Instance()->ViewProjectionMatrix;
FFXIVClientStructs.FFXIV.Client.Game.Camera* activeCamera = Control.Instance()->CameraManager.GetActiveCamera();
FFXIVClientStructs.FFXIV.Client.Graphics.Render.Camera* ptr = ((activeCamera != null) ? activeCamera->SceneCamera.RenderCamera : null);
if (ptr == null)
{
nearPlane = new Vector4(viewProj.M13, viewProj.M23, viewProj.M33, viewProj.M43);
return;
}
if (!Matrix4x4.Invert(ptr->ProjectionMatrix, out var result))
{
nearPlane = new Vector4(viewProj.M13, viewProj.M23, viewProj.M33, viewProj.M43);
return;
}
Matrix4x4 matrix4x = viewProj * result;
nearPlane = new Vector4(matrix4x.M13, matrix4x.M23, matrix4x.M33, matrix4x.M43 + ptr->NearPlane);
}
public void DrawStroke(List<Vector3> world, float thickness, uint color, bool closed = false, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, AxLineCap cap = AxLineCap.Butt, AxLineJoin join = AxLineJoin.None)
{
if (world.Count != 0)
{
for (int i = 1; i < world.Count; i++)
{
DrawStroke(world[i - 1], world[i], thickness, color);
}
if (closed)
{
DrawStroke(world[world.Count - 1], world[0], thickness, color);
}
}
}
public void DrawStroke(Vector3 start, Vector3 stop, float thickness, uint color)
{
if (FallbackClipMath.ClipLineToPlane(nearPlane, ref start, ref stop, out var _) != FallbackClipMath.LineClipStatus.NotVisible)
{
drawList.PathClear();
drawList.PathLineTo(FallbackClipMath.WorldToScreenOld(in viewProj, in start));
drawList.PathLineTo(FallbackClipMath.WorldToScreenOld(in viewProj, in stop));
drawList.PathStroke(color, ImDrawFlags.None, thickness);
}
}
}

View file

@ -0,0 +1,64 @@
using System;
using System.Numerics;
using System.Text;
using Auspex.Vfx.Native;
using Dalamud.Game.ClientState.Objects.Types;
namespace Auspex.Vfx.Internal;
public sealed class ActorBoundVfx : IDisposable
{
internal IGameObject target;
internal IGameObject source;
internal Vector3 scale;
internal Vector4 color;
internal unsafe VfxData* data;
public static ActorBoundVfx Create(string path, IGameObject target, IGameObject source, Vector3 scale, Vector4 color)
{
return new ActorBoundVfx(path, target, source, scale, color);
}
private unsafe ActorBoundVfx(string path, IGameObject target, IGameObject source, Vector3 scale, Vector4 color)
{
this.target = target;
this.source = source;
this.scale = scale;
this.color = color;
data = CreateGameObjectVfxInternal(path, target, source, scale, color);
}
public unsafe void Dispose()
{
if (data != null && data->Instance != null)
{
VfxNativeBridge.DestroyVfx(data);
}
data = null;
GC.SuppressFinalize(this);
}
private unsafe static VfxData* CreateGameObjectVfxInternal(string path, IGameObject target, IGameObject source, Vector3 scale, Vector4 color)
{
fixed (byte* bytes = Encoding.UTF8.GetBytes(path + "\0"))
{
VfxData* intPtr = VfxNativeBridge.CreateGameObjectVfxInternal(bytes, target.Address, source.Address, 1f, 0, 0, 1);
intPtr->Instance->Scale = scale;
intPtr->Instance->Color = color;
return intPtr;
}
}
public unsafe void UpdateColor(Vector4 color)
{
if (!(this.color == color))
{
this.color = color;
VfxNativeBridge.UpdateVfxColor(data->Instance, color.X, color.Y, color.Z, color.W);
}
}
}

View file

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Auspex.Vfx.Internal;
internal sealed class FrameLifetimeTracker<T> : IDisposable where T : IDisposable
{
private Dictionary<(string, string), T> prevActive;
private Dictionary<(string, string), T> currActive;
internal FrameLifetimeTracker()
{
prevActive = new Dictionary<(string, string), T>();
currActive = new Dictionary<(string, string), T>();
}
internal bool IsTouched((string, string) key)
{
return currActive.ContainsKey(key);
}
internal bool TryTouchExisting((string, string) key, [MaybeNullWhen(false)] out T o)
{
if (prevActive.TryGetValue(key, out o))
{
prevActive.Remove(key);
currActive.Add(key, o);
return true;
}
return false;
}
internal void TouchNew((string, string) key, T t)
{
currActive.Add(key, t);
}
internal bool Remove((string, string) key)
{
if (currActive.Remove(key, out var value))
{
value.Dispose();
return true;
}
if (prevActive.Remove(key, out var value2))
{
value2.Dispose();
return true;
}
return false;
}
public void Update()
{
foreach (KeyValuePair<(string, string), T> item in prevActive)
{
item.Value.Dispose();
}
prevActive.Clear();
Dictionary<(string, string), T> dictionary = prevActive;
prevActive = currActive;
currActive = dictionary;
}
internal void ForEachActive(Action<T> action)
{
foreach (KeyValuePair<(string, string), T> item in prevActive)
{
action(item.Value);
}
}
public void Dispose()
{
foreach (KeyValuePair<(string, string), T> item in prevActive)
{
item.Value.Dispose();
}
foreach (KeyValuePair<(string, string), T> item2 in currActive)
{
item2.Value.Dispose();
}
GC.SuppressFinalize(this);
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Auspex.Vfx.Internal;
internal sealed class PinnedAlignedMatrix : IDisposable
{
private IntPtr rawptr;
private IntPtr aligned;
public unsafe Matrix4x4* Get => (Matrix4x4*)aligned;
public unsafe PinnedAlignedMatrix()
{
rawptr = Marshal.AllocHGlobal(sizeof(Matrix4x4) + 8);
aligned = new IntPtr(16 * (((long)(nint)rawptr + 15L) / 16));
}
public void Dispose()
{
Marshal.FreeHGlobal(rawptr);
GC.SuppressFinalize(this);
}
}

View file

@ -0,0 +1,150 @@
using System;
using System.Numerics;
using System.Text;
using Auspex.Vfx.Native;
namespace Auspex.Vfx.Internal;
public sealed class VfxInstance : IDisposable
{
internal string? path;
internal Vector3 position;
internal Vector3 size;
internal float rotation;
internal Vector4 color;
internal unsafe VfxData* data;
internal bool libOwned;
public unsafe bool IsValid
{
get
{
if (data != null)
{
return data->Instance != null;
}
return false;
}
}
public unsafe static VfxInstance Create(string path, Vector3 position, Vector3 size, float rotation, Vector4? color = null)
{
VfxData* ptr = CreateVfxInternal(path, position, size, rotation, color ?? Vector4.One);
return new VfxInstance(path, ptr, position, size, rotation, color ?? Vector4.One)
{
libOwned = true
};
}
public unsafe static VfxInstance Wrap(VfxData* data, Vector3 position, Vector3 size, float rotation, Vector4? color = null)
{
return new VfxInstance(null, data, position, size, rotation, color ?? Vector4.One);
}
private unsafe VfxInstance(string? path, VfxData* data, Vector3 position, Vector3 size, float rotation, Vector4 color)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
this.path = path;
this.data = data;
this.position = position;
this.size = size;
this.rotation = rotation;
this.color = color;
}
public unsafe void Dispose()
{
if (libOwned && IsValid)
{
VfxNativeBridge.DestroyVfx(data);
}
data = null;
GC.SuppressFinalize(this);
}
private unsafe static VfxData* CreateVfxInternal(string path, Vector3 position, Vector3 size, float rotation, Vector4 color)
{
byte[] bytes = Encoding.UTF8.GetBytes(path + "\0");
VfxInitData vfxInitData = default(VfxInitData);
VfxNativeBridge.VfxInitDataCtor(&vfxInitData);
fixed (byte* ptr = bytes)
{
VfxData* intPtr = VfxNativeBridge.CreateVfx(ptr, &vfxInitData, 2, 0, position.X, position.Y, position.Z, size.X, size.Y, size.Z, rotation, 1f, -1);
intPtr->Instance->Color = color;
return intPtr;
}
}
public void UpdatePosition(Vector3 position)
{
UpdateTransform(position, size, rotation);
}
public void UpdateScale(Vector3 scale)
{
UpdateTransform(position, scale, rotation);
}
public void UpdateRotation(float rotation)
{
UpdateTransform(position, size, rotation);
}
public unsafe void UpdateTransform(Vector3 position, Vector3 size, float rotation)
{
MaybeRefreshVfxResourceInstance();
if (!IsValid || (this.position == position && this.rotation == rotation && this.size == size))
{
return;
}
this.position = position;
this.size = size;
this.rotation = rotation;
using PinnedAlignedMatrix pinnedAlignedMatrix = new PinnedAlignedMatrix();
Matrix4x4* get = pinnedAlignedMatrix.Get;
get->M11 = size.X;
get->M12 = 0f;
get->M13 = 0f;
get->M14 = 0f;
get->M21 = 0f;
get->M22 = size.Y;
get->M23 = 0f;
get->M24 = 0f;
get->M31 = 0f;
get->M32 = 0f;
get->M33 = size.Z;
get->M34 = 0f;
VfxNativeBridge.RotateMatrix(get, rotation);
get->M41 = position.X;
get->M42 = position.Y;
get->M43 = position.Z;
get->M44 = 1f;
VfxNativeBridge.UpdateVfxTransform(data->Instance, get);
}
public unsafe void UpdateColor(Vector4 color)
{
if (IsValid && !(this.color == color))
{
this.color = color;
VfxNativeBridge.UpdateVfxColor(data->Instance, color.X, color.Y, color.Z, color.W);
}
}
private unsafe void MaybeRefreshVfxResourceInstance()
{
if (!IsValid && libOwned && path != null)
{
data = CreateVfxInternal(path, position, size, rotation, color);
}
}
}

View file

@ -0,0 +1,68 @@
using System;
using System.Numerics;
using Auspex.Vfx.Native;
namespace Auspex.Vfx.Internal;
public static class VfxNativeBridge
{
public unsafe delegate VfxInitData* VfxInitDataCtorDelegate(VfxInitData* self);
public unsafe delegate VfxData* CreateVfxDelegate(byte* path, VfxInitData* init, byte a3, byte a4, float originX, float originY, float originZ, float sizeX, float sizeY, float sizeZ, float angle, float duration, int a13);
public unsafe delegate VfxData* CreateGameObjectVfxDelegate(byte* path, IntPtr target, IntPtr source, float a4, byte a5, ushort a6, byte a7);
public unsafe delegate void DestroyVfxDataDelegate(VfxData* self);
public unsafe delegate long UpdateVfxTransformDelegate(VfxResourceInstance* vfxInstance, Matrix4x4* transform);
public unsafe delegate long UpdateVfxColorDelegate(VfxResourceInstance* vfxInstance, float r, float g, float b, float a);
public unsafe delegate void RotateMatrixDelegate(Matrix4x4* matrix, float rotation);
public const string VfxInitDataCtorSig = "E8 ?? ?? ?? ?? 8D 57 06 48 8D 4C 24 ??";
public static VfxInitDataCtorDelegate VfxInitDataCtor;
public const string CreateVfxSig = "E8 ?? ?? ?? ?? 48 8B D8 48 8D 95";
public static CreateVfxDelegate CreateVfx;
public const string CreateGameObjectVfxSig = "E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 74 27 B2 01";
public static CreateGameObjectVfxDelegate CreateGameObjectVfxInternal;
public const string DestroyVfxSig = "E8 ?? ?? ?? ?? 4D 89 A4 DE ?? ?? ?? ??";
public static DestroyVfxDataDelegate DestroyVfx;
public const string UpdateVfxTransformSig = "E8 ?? ?? ?? ?? EB 19 48 8B 0B";
public static UpdateVfxTransformDelegate UpdateVfxTransform;
public const string UpdateVfxColorSig = "E8 ?? ?? ?? ?? 8B 4B F3";
public static UpdateVfxColorDelegate UpdateVfxColor;
public const string RotateMatrixSig = "E8 ?? ?? ?? ?? 4C 8D 76 20";
public static RotateMatrixDelegate RotateMatrix;
internal static void Initialize()
{
VfxInitDataCtorDelegate vfxInitDataCtor = SigScanHelper.Scan<VfxInitDataCtorDelegate>("E8 ?? ?? ?? ?? 8D 57 06 48 8D 4C 24 ??", "VfxInitDataCtor");
CreateVfxDelegate createVfx = SigScanHelper.Scan<CreateVfxDelegate>("E8 ?? ?? ?? ?? 48 8B D8 48 8D 95", "CreateVfx");
CreateGameObjectVfxDelegate createGameObjectVfxInternal = SigScanHelper.Scan<CreateGameObjectVfxDelegate>("E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 74 27 B2 01", "CreateGameObjectVfxInternal");
DestroyVfxDataDelegate destroyVfx = SigScanHelper.Scan<DestroyVfxDataDelegate>("E8 ?? ?? ?? ?? 4D 89 A4 DE ?? ?? ?? ??", "DestroyVfx");
UpdateVfxTransformDelegate updateVfxTransform = SigScanHelper.Scan<UpdateVfxTransformDelegate>("E8 ?? ?? ?? ?? EB 19 48 8B 0B", "UpdateVfxTransform");
UpdateVfxColorDelegate updateVfxColor = SigScanHelper.Scan<UpdateVfxColorDelegate>("E8 ?? ?? ?? ?? 8B 4B F3", "UpdateVfxColor");
RotateMatrixDelegate rotateMatrix = SigScanHelper.Scan<RotateMatrixDelegate>("E8 ?? ?? ?? ?? 4C 8D 76 20", "RotateMatrix");
VfxInitDataCtor = vfxInitDataCtor;
CreateVfx = createVfx;
CreateGameObjectVfxInternal = createGameObjectVfxInternal;
DestroyVfx = destroyVfx;
UpdateVfxTransform = updateVfxTransform;
UpdateVfxColor = updateVfxColor;
RotateMatrix = rotateMatrix;
}
}

View file

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Auspex.Vfx.Native;
[StructLayout(LayoutKind.Explicit, Size = 480)]
public struct VfxData
{
[FieldOffset(440)]
public unsafe VfxResourceInstance* Instance;
}

View file

@ -0,0 +1,8 @@
using System.Runtime.InteropServices;
namespace Auspex.Vfx.Native;
[StructLayout(LayoutKind.Explicit, Size = 416)]
public struct VfxInitData
{
}

View file

@ -0,0 +1,14 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Auspex.Vfx.Native;
[StructLayout(LayoutKind.Explicit, Size = 192)]
public struct VfxResourceInstance
{
[FieldOffset(144)]
public Vector3 Scale;
[FieldOffset(160)]
public Vector4 Color;
}

29
Auspex/Auspex.csproj Normal file
View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Auspex</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup />
<ItemGroup />
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Dalamud.dll</HintPath>
</Reference>
<Reference Include="Dalamud.Bindings.ImGui">
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Dalamud.Bindings.ImGui.dll</HintPath>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\FFXIVClientStructs.dll</HintPath>
</Reference>
<Reference Include="TerraFX.Interop.Windows">
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\TerraFX.Interop.Windows.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,8 @@
namespace Auspex;
public enum AlphaBlendMode
{
None,
Add,
Max
}

View file

@ -0,0 +1,12 @@
using System;
namespace Auspex;
public static class AlphaBlendModes
{
public static readonly string[] Names = new string[3] { "None", "Add", "Maximum" };
public static readonly string[] Tooltips = new string[3] { "Overlapping elements are not blended.\nWhatever is drawn last overwrites anything it overlaps.", "Overlapping elements are blended by adding their alpha values.\nThis makes it easier to see areas where elements overlap.", "Overlapping elements are blended using the most opaque alpha.\nThis makes it easier to see through overlapping areas." };
public static readonly AlphaBlendMode[] Values = (AlphaBlendMode[])Enum.GetValues(typeof(AlphaBlendMode));
}

View file

@ -0,0 +1,189 @@
using System;
using Auspex.Lighting.Internal;
using Auspex.Rendering.Direct3D;
using Auspex.Vfx.Internal;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.NativeWrapper;
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
namespace Auspex;
public class AuspexService
{
private static Direct3DRenderer? _dxRenderer;
private static VfxRenderer? _vfxRenderer;
private static LightRenderer? _lightRenderer;
internal static AxDrawList? DrawList;
internal static AxDrawHints Hints;
private static bool _frameworkUpdateRegistered;
private static bool ServicesInitialized;
[PluginService]
public static IDalamudPluginInterface PluginInterface { get; private set; }
[PluginService]
public static IGameGui GameGui { get; private set; }
[PluginService]
public static ICondition Condition { get; private set; }
[PluginService]
public static IPluginLog Log { get; private set; }
[PluginService]
public static ISigScanner SigScanner { get; private set; }
[PluginService]
internal static IFramework Framework { get; private set; }
[PluginService]
internal static IClientState ClientState { get; private set; }
public static VfxRenderer? VfxRenderer => _vfxRenderer;
public static LightRenderer? LightRenderer => _lightRenderer;
public static bool FanDegraded => _dxRenderer?.FanDegraded ?? false;
public static bool StrokeDegraded => _dxRenderer?.StrokeDegraded ?? false;
public static bool FSPDegraded => _dxRenderer?.FSPDegraded ?? false;
public static bool TexturedQuadDegraded => _dxRenderer?.TexturedQuadDegraded ?? false;
public static bool IsDrawingAvailable => _dxRenderer != null;
public static void Initialize(IDalamudPluginInterface pluginInterface)
{
InitializeDxRenderer(pluginInterface);
InitializeVfxRenderer(pluginInterface);
InitializeLightRenderer(pluginInterface);
}
public static void InitializeDxRenderer(IDalamudPluginInterface pluginInterface)
{
ArgumentNullException.ThrowIfNull((object)pluginInterface, "pluginInterface");
InitializePluginServices(pluginInterface);
_dxRenderer = new Direct3DRenderer();
ClientState.TerritoryChanged += OnTerritoryChanged;
}
public static void InitializeVfxRenderer(IDalamudPluginInterface pluginInterface)
{
ArgumentNullException.ThrowIfNull((object)pluginInterface, "pluginInterface");
InitializePluginServices(pluginInterface);
_vfxRenderer = new VfxRenderer();
VfxNativeBridge.Initialize();
EnsureFrameworkUpdate();
}
public static void InitializeLightRenderer(IDalamudPluginInterface pluginInterface)
{
ArgumentNullException.ThrowIfNull((object)pluginInterface, "pluginInterface");
InitializePluginServices(pluginInterface);
_lightRenderer = new LightRenderer();
LightNativeBridge.Initialize();
EnsureFrameworkUpdate();
}
private static void EnsureFrameworkUpdate()
{
if (!_frameworkUpdateRegistered)
{
Framework.Update += Update;
_frameworkUpdateRegistered = true;
}
}
private static void InitializePluginServices(IDalamudPluginInterface pluginInterface)
{
if (!ServicesInitialized)
{
pluginInterface.Create<AuspexService>(Array.Empty<object>());
ServicesInitialized = true;
}
}
public static void Dispose()
{
ClientState.TerritoryChanged -= OnTerritoryChanged;
_dxRenderer?.Dispose();
_dxRenderer = null;
_vfxRenderer?.Dispose();
_vfxRenderer = null;
_lightRenderer?.Dispose();
_lightRenderer = null;
if (_frameworkUpdateRegistered)
{
Framework.Update -= Update;
_frameworkUpdateRegistered = false;
}
}
internal static void Update(IFramework framework)
{
_vfxRenderer?.Update();
_lightRenderer?.Update();
}
private static void OnTerritoryChanged(uint territoryId)
{
AxTerrainQuery.ClearCache();
}
public static AxDrawList GetDrawList()
{
if (DrawList == null)
{
throw new InvalidOperationException("GetDrawList called without Draw");
}
return DrawList;
}
public static AxDrawList? Draw(ImDrawListPtr? imguidrawlist = null, AxDrawHints? hints = null)
{
if (_dxRenderer == null)
{
return null;
}
Hints = hints ?? new AxDrawHints();
if (!Hints.DrawInCutscene && IsInCutscene())
{
return null;
}
if (!Hints.DrawWhenFaded && IsFaded())
{
return null;
}
return DrawList = new AxDrawList(imguidrawlist, _dxRenderer);
}
private static bool IsInCutscene()
{
if (!Condition[ConditionFlag.OccupiedInCutSceneEvent])
{
return Condition[ConditionFlag.WatchingCutscene78];
}
return true;
}
private static bool IsFaded()
{
AtkUnitBasePtr addonByName = GameGui.GetAddonByName("FadeMiddle");
AtkUnitBasePtr addonByName2 = GameGui.GetAddonByName("FadeBlack");
if (!addonByName.IsVisible)
{
return addonByName2.IsVisible;
}
return true;
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxAngularGradient : IPctGradient
{
private readonly Vector3 _center;
private readonly float _minAngle;
private readonly float _invRange;
private readonly Vector4 _colorStart;
private readonly Vector4 _colorEnd;
public AxAngularGradient(Vector3 center, uint colorStart, uint colorEnd, float minAngle = 0f, float maxAngle = (float)Math.PI * 2f)
{
_center = center;
_minAngle = minAngle;
_invRange = 1f / (maxAngle - minAngle);
_colorStart = colorStart.ToVector4();
_colorEnd = colorEnd.ToVector4();
}
public uint ColorAt(Vector3 point)
{
float amount = Math.Clamp((MathF.Atan2(point.Z - _center.Z, point.X - _center.X) - (float)Math.PI / 2f - _minAngle) * _invRange % 1f, 0f, 1f);
return Vector4.Lerp(_colorStart, _colorEnd, amount).ToUint();
}
}

View file

@ -0,0 +1,182 @@
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Auspex;
public readonly struct AxAnimation
{
private readonly long _startTimestamp;
private readonly float _duration;
private readonly AxEasing _easing;
private readonly AxRepeatMode _repeatMode;
private readonly float _delay;
public float Progress
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_duration <= 0f)
{
return 1f;
}
float num = (float)Stopwatch.GetElapsedTime(_startTimestamp).TotalSeconds - _delay;
if (num < 0f)
{
return 0f;
}
float num2 = num / _duration;
switch (_repeatMode)
{
case AxRepeatMode.Repeat:
num2 -= MathF.Floor(num2);
break;
case AxRepeatMode.PingPong:
{
int num3 = (int)MathF.Floor(num2);
num2 -= MathF.Floor(num2);
if (num3 % 2 == 1)
{
num2 = 1f - num2;
}
break;
}
default:
num2 = Math.Clamp(num2, 0f, 1f);
break;
}
return ApplyEasing(num2, _easing);
}
}
public bool IsComplete
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_repeatMode != AxRepeatMode.None)
{
return false;
}
if (_duration <= 0f)
{
return true;
}
return (float)Stopwatch.GetElapsedTime(_startTimestamp).TotalSeconds >= _delay + _duration;
}
}
private AxAnimation(float duration, AxEasing easing, AxRepeatMode repeatMode, float delay)
{
_startTimestamp = Stopwatch.GetTimestamp();
_duration = duration;
_easing = easing;
_repeatMode = repeatMode;
_delay = delay;
}
public static AxAnimation Start(float duration, AxEasing easing = AxEasing.Linear, AxRepeatMode repeatMode = AxRepeatMode.None, float delay = 0f)
{
return new AxAnimation(duration, easing, repeatMode, delay);
}
public static AxAnimation Start(float duration, AxEasing easing, bool repeat)
{
return new AxAnimation(duration, easing, repeat ? AxRepeatMode.Repeat : AxRepeatMode.None, 0f);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint FadeIn(uint color)
{
return AxColor.WithAlpha(color, Progress);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint FadeOut(uint color)
{
return AxColor.WithAlpha(color, 1f - Progress);
}
public uint Pulse(uint color, float minAlpha = 0f)
{
float num = (float)((color >> 24) & 0xFF) / 255f;
float num2 = 0.5f + 0.5f * MathF.Cos(Progress * 2f * (float)Math.PI);
uint num3 = (uint)(Math.Clamp(minAlpha + (num - minAlpha) * num2, 0f, 1f) * 255f);
return (color & 0xFFFFFF) | (num3 << 24);
}
public uint ColorLerp(uint from, uint to)
{
return AxColor.Lerp(from, to, Progress);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Lerp(float from, float to)
{
return from + (to - from) * Progress;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector2 Lerp(Vector2 from, Vector2 to)
{
return from + (to - from) * Progress;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 Lerp(Vector3 from, Vector3 to)
{
return from + (to - from) * Progress;
}
internal static float ApplyEasing(float t, AxEasing easing)
{
return easing switch
{
AxEasing.EaseIn => t * t,
AxEasing.EaseOut => 1f - (1f - t) * (1f - t),
AxEasing.EaseInOut => t * t * (3f - 2f * t),
AxEasing.CubicIn => t * t * t,
AxEasing.CubicOut => 1f - (1f - t) * (1f - t) * (1f - t),
AxEasing.CubicInOut => (t < 0.5f) ? (4f * t * t * t) : (1f - MathF.Pow(-2f * t + 2f, 3f) / 2f),
AxEasing.SineIn => 1f - MathF.Cos(t * (float)Math.PI / 2f),
AxEasing.SineOut => MathF.Sin(t * (float)Math.PI / 2f),
AxEasing.SineInOut => (0f - (MathF.Cos((float)Math.PI * t) - 1f)) / 2f,
AxEasing.ExponentialIn => (t == 0f) ? 0f : MathF.Pow(2f, 10f * t - 10f),
AxEasing.ExponentialOut => (t == 1f) ? 1f : (1f - MathF.Pow(2f, -10f * t)),
AxEasing.ExponentialInOut => (t == 0f) ? 0f : ((t == 1f) ? 1f : ((t < 0.5f) ? (MathF.Pow(2f, 20f * t - 10f) / 2f) : ((2f - MathF.Pow(2f, -20f * t + 10f)) / 2f))),
AxEasing.BackIn => 2.70158f * t * t * t - 1.70158f * t * t,
AxEasing.BackOut => 1f + 2.70158f * (t - 1f) * (t - 1f) * (t - 1f) + 1.70158f * (t - 1f) * (t - 1f),
AxEasing.BackInOut => (t < 0.5f) ? (2f * t * (2f * t) * (7.189819f * t - 2.5949094f) / 2f) : (((2f * t - 2f) * (2f * t - 2f) * (3.5949094f * (2f * t - 2f) + 2.5949094f) + 2f) / 2f),
AxEasing.ElasticIn => (t == 0f) ? 0f : ((t == 1f) ? 1f : ((0f - MathF.Pow(2f, 10f * t - 10f)) * MathF.Sin((10f * t - 10.75f) * ((float)Math.PI * 2f / 3f)))),
AxEasing.ElasticOut => (t == 0f) ? 0f : ((t == 1f) ? 1f : (MathF.Pow(2f, -10f * t) * MathF.Sin((10f * t - 0.75f) * ((float)Math.PI * 2f / 3f)) + 1f)),
AxEasing.ElasticInOut => (t == 0f) ? 0f : ((t == 1f) ? 1f : ((t < 0.5f) ? ((0f - MathF.Pow(2f, 20f * t - 10f) * MathF.Sin((20f * t - 11.125f) * ((float)Math.PI * 4f / 9f))) / 2f) : (MathF.Pow(2f, -20f * t + 10f) * MathF.Sin((20f * t - 11.125f) * ((float)Math.PI * 4f / 9f)) / 2f + 1f))),
AxEasing.BounceIn => 1f - BounceOut(1f - t),
AxEasing.BounceOut => BounceOut(t),
AxEasing.BounceInOut => (t < 0.5f) ? ((1f - BounceOut(1f - 2f * t)) / 2f) : ((1f + BounceOut(2f * t - 1f)) / 2f),
_ => t,
};
}
private static float BounceOut(float t)
{
if (t < 0.36363637f)
{
return 7.5625f * t * t;
}
if (t < 0.72727275f)
{
return 7.5625f * (t -= 0.54545456f) * t + 0.75f;
}
if (t < 0.90909094f)
{
return 7.5625f * (t -= 0.8181818f) * t + 0.9375f;
}
return 7.5625f * (t -= 21f / 22f) * t + 63f / 64f;
}
}

View file

@ -0,0 +1,3 @@
namespace Auspex;
public readonly record struct AxAnimationPhase(float Duration, AxEasing Easing = AxEasing.Linear);

View file

@ -0,0 +1,116 @@
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Auspex;
public readonly struct AxAnimationSequence
{
private readonly long _startTimestamp;
private readonly AxAnimationPhase[] _phases;
private readonly float _totalDuration;
public int Phase
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return GetCurrent().phase;
}
}
public float PhaseProgress
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return GetCurrent().progress;
}
}
public bool IsComplete
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_phases == null || _phases.Length == 0)
{
return true;
}
return (float)Stopwatch.GetElapsedTime(_startTimestamp).TotalSeconds >= _totalDuration;
}
}
private AxAnimationSequence(AxAnimationPhase[] phases)
{
_startTimestamp = Stopwatch.GetTimestamp();
_phases = phases;
_totalDuration = 0f;
foreach (AxAnimationPhase axAnimationPhase in phases)
{
_totalDuration += axAnimationPhase.Duration;
}
}
public static AxAnimationSequence Start(params AxAnimationPhase[] phases)
{
return new AxAnimationSequence(phases);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint FadeIn(uint color)
{
return AxColor.WithAlpha(color, PhaseProgress);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint FadeOut(uint color)
{
return AxColor.WithAlpha(color, 1f - PhaseProgress);
}
public uint ColorLerp(uint from, uint to)
{
return AxColor.Lerp(from, to, PhaseProgress);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Lerp(float from, float to)
{
return from + (to - from) * PhaseProgress;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector2 Lerp(Vector2 from, Vector2 to)
{
return from + (to - from) * PhaseProgress;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 Lerp(Vector3 from, Vector3 to)
{
return from + (to - from) * PhaseProgress;
}
private (int phase, float progress) GetCurrent()
{
if (_phases == null || _phases.Length == 0)
{
return (phase: 0, progress: 1f);
}
float num = (float)Stopwatch.GetElapsedTime(_startTimestamp).TotalSeconds;
float num2 = 0f;
for (int i = 0; i < _phases.Length; i++)
{
if (num < num2 + _phases[i].Duration)
{
float t = ((_phases[i].Duration > 0f) ? ((num - num2) / _phases[i].Duration) : 1f);
return (phase: i, progress: AxAnimation.ApplyEasing(t, _phases[i].Easing));
}
num2 += _phases[i].Duration;
}
return (phase: _phases.Length - 1, progress: 1f);
}
}

156
Auspex/Auspex/AxColor.cs Normal file
View file

@ -0,0 +1,156 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using Auspex.Rendering.Direct3D;
using Dalamud.Bindings.ImGui;
namespace Auspex;
public static class AxColor
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint WithAlpha(uint color, byte alpha)
{
return (color & 0xFFFFFF) | (uint)(alpha << 24);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint WithAlpha(uint color, float alphaFactor)
{
uint num = (color >> 24) & 0xFF;
num = (uint)((float)num * Math.Clamp(alphaFactor, 0f, 1f));
return (color & 0xFFFFFF) | (num << 24);
}
public static uint FromHSV(float h, float s, float v, float a = 1f)
{
float outR = 0f;
float outG = 0f;
float outB = 0f;
ImGui.ColorConvertHSVtoRGB(h, s, v, ref outR, ref outG, ref outB);
return new Vector4(outR, outG, outB, a).ToUint();
}
public static (float H, float S, float V, float A) ToHSV(uint color)
{
Vector4 vector = color.ToVector4();
float outH = 0f;
float outS = 0f;
float outV = 0f;
ImGui.ColorConvertRGBtoHSV(vector.X, vector.Y, vector.Z, ref outH, ref outS, ref outV);
return (H: outH, S: outS, V: outV, A: vector.W);
}
public static uint FromHSL(float h, float s, float l, float a = 1f)
{
HSLToRGB(h, s, l, out var r, out var g, out var b);
return new Vector4(r, g, b, a).ToUint();
}
public static (float H, float S, float L, float A) ToHSL(uint color)
{
Vector4 vector = color.ToVector4();
RGBToHSL(vector.X, vector.Y, vector.Z, out var h, out var s, out var l);
return (H: h, S: s, L: l, A: vector.W);
}
public static uint Brighten(uint color, float amount)
{
Vector4 vector = color.ToVector4();
RGBToHSL(vector.X, vector.Y, vector.Z, out var h, out var s, out var l);
l = Math.Clamp(l + amount, 0f, 1f);
HSLToRGB(h, s, l, out var r, out var g, out var b);
return new Vector4(r, g, b, vector.W).ToUint();
}
public static uint Darken(uint color, float amount)
{
return Brighten(color, 0f - amount);
}
public static uint Saturate(uint color, float amount)
{
return Desaturate(color, 0f - amount);
}
public static uint Desaturate(uint color, float amount)
{
Vector4 vector = color.ToVector4();
RGBToHSL(vector.X, vector.Y, vector.Z, out var h, out var s, out var l);
s = Math.Clamp(s - amount, 0f, 1f);
HSLToRGB(h, s, l, out var r, out var g, out var b);
return new Vector4(r, g, b, vector.W).ToUint();
}
public static uint Lerp(uint from, uint to, float t)
{
Vector4 value = from.ToVector4();
Vector4 value2 = to.ToVector4();
return Vector4.Lerp(value, value2, t).ToUint();
}
private static void RGBToHSL(float r, float g, float b, out float h, out float s, out float l)
{
float num = MathF.Max(r, MathF.Max(g, b));
float num2 = MathF.Min(r, MathF.Min(g, b));
l = (num + num2) * 0.5f;
if (num == num2)
{
h = (s = 0f);
return;
}
float num3 = num - num2;
s = ((l > 0.5f) ? (num3 / (2f - num - num2)) : (num3 / (num + num2)));
if (num == r)
{
h = ((g - b) / num3 + ((g < b) ? 6f : 0f)) / 6f;
}
else if (num == g)
{
h = ((b - r) / num3 + 2f) / 6f;
}
else
{
h = ((r - g) / num3 + 4f) / 6f;
}
}
private static void HSLToRGB(float h, float s, float l, out float r, out float g, out float b)
{
if (s == 0f)
{
r = (g = (b = l));
return;
}
float num = ((l < 0.5f) ? (l * (1f + s)) : (l + s - l * s));
float p = 2f * l - num;
r = HueToRGB(p, num, h + 1f / 3f);
g = HueToRGB(p, num, h);
b = HueToRGB(p, num, h - 1f / 3f);
}
private static float HueToRGB(float p, float q, float t)
{
if (t < 0f)
{
t += 1f;
}
if (t > 1f)
{
t -= 1f;
}
if (t < 1f / 6f)
{
return p + (q - p) * 6f * t;
}
if (t < 0.5f)
{
return q;
}
if (t < 2f / 3f)
{
return p + (q - p) * (2f / 3f - t) * 6f;
}
return p;
}
}

View file

@ -0,0 +1,22 @@
namespace Auspex;
public record struct AxDrawHints(bool DrawInCutscene = false, bool DrawWhenFaded = false, bool AutoDraw = true, byte MaxAlpha = byte.MaxValue, AlphaBlendMode AlphaBlendMode = AlphaBlendMode.Add, bool ClipNativeUI = true, AxDxParams DefaultDxParams = default(AxDxParams))
{
public float MaxAlphaFraction => (float)(int)MaxAlpha / 255f;
public AxDrawHints(bool DrawInCutscene = false, bool DrawWhenFaded = false, bool AutoDraw = true, byte MaxAlpha = byte.MaxValue, AlphaBlendMode AlphaBlendMode = AlphaBlendMode.Add, bool ClipNativeUI = true, AxDxParams DefaultDxParams = default(AxDxParams))
{
this.DrawInCutscene = DrawInCutscene;
this.DrawWhenFaded = DrawWhenFaded;
this.AutoDraw = AutoDraw;
this.MaxAlpha = MaxAlpha;
this.AlphaBlendMode = AlphaBlendMode;
this.ClipNativeUI = ClipNativeUI;
this.DefaultDxParams = DefaultDxParams;
}
public AxDrawHints()
: this(DrawInCutscene: false, DrawWhenFaded: false, AutoDraw: true, byte.MaxValue, AlphaBlendMode.Add, ClipNativeUI: true, default(AxDxParams))
{
}
}

1611
Auspex/Auspex/AxDrawList.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,333 @@
using System;
using System.Numerics;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.Textures.TextureWraps;
namespace Auspex;
public static class AxDrawListObjectExtensions
{
public static Vector3 ResolvePosition(IGameObject target, Vector3 localOffset = default(Vector3))
{
Vector3 position = target.Position;
if (localOffset == Vector3.Zero)
{
return position;
}
float num = MathF.Cos(target.Rotation);
float num2 = MathF.Sin(target.Rotation);
return new Vector3(position.X - localOffset.X * num - localOffset.Z * num2, position.Y + localOffset.Y, position.Z - localOffset.X * num2 + localOffset.Z * num);
}
public static void AddDot(this AxDrawList drawList, IGameObject target, float radiusPixels, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u)
{
drawList.AddDot(ResolvePosition(target, localOffset), radiusPixels, color, numSegments);
}
public static void AddText(this AxDrawList drawList, IGameObject target, uint color, string text, Vector3 localOffset = default(Vector3), float scale = 1f, uint? outlineColor = null, float outlineThickness = 1f, uint? shadowColor = null, Vector2? shadowOffset = null)
{
drawList.AddText(ResolvePosition(target, localOffset), color, text, scale, outlineColor, outlineThickness, shadowColor, shadowOffset);
}
public static void AddWorldText(this AxDrawList drawList, IGameObject target, uint color, string text, Vector3 localOffset = default(Vector3), float scale = 1f, uint? outlineColor = null, float outlineThickness = 1f, uint? shadowColor = null, Vector2? shadowOffset = null)
{
drawList.AddWorldText(ResolvePosition(target, localOffset), color, text, scale, outlineColor, outlineThickness, shadowColor, shadowOffset);
}
public static void AddTexture(this AxDrawList drawList, IGameObject target, IDalamudTextureWrap texture, Vector3 localOffset = default(Vector3), float scale = 1f, uint color = uint.MaxValue)
{
drawList.AddTexture(ResolvePosition(target, localOffset), texture, scale, color);
}
public static void AddBillboard(this AxDrawList drawList, IGameObject target, IDalamudTextureWrap texture, float width, float height, Vector3 localOffset = default(Vector3), uint color = uint.MaxValue, float rotation = 0f, Vector2 uvMin = default(Vector2), Vector2 uvMax = default(Vector2))
{
drawList.AddBillboard(ResolvePosition(target, localOffset), texture, width, height, color, rotation, uvMin, uvMax);
}
public static void AddBillboard(this AxDrawList drawList, IGameObject target, IDalamudTextureWrap texture, Vector3 localOffset = default(Vector3), float scale = 1f, uint color = uint.MaxValue, float rotation = 0f, Vector2 uvMin = default(Vector2), Vector2 uvMax = default(Vector2))
{
drawList.AddBillboard(ResolvePosition(target, localOffset), texture, scale, color, rotation, uvMin, uvMax);
}
public static void AddCircle(this AxDrawList drawList, IGameObject target, float radius, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddCircle(ResolvePosition(target, localOffset), radius, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddCircleFilled(this AxDrawList drawList, IGameObject target, float radius, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u)
{
drawList.AddCircleFilled(ResolvePosition(target, localOffset), radius, color, colorEnd, numSegments);
}
public static void AddCircleFilled<T>(this AxDrawList drawList, IGameObject target, float radius, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u) where T : IPctGradient
{
drawList.AddCircleFilled(ResolvePosition(target, localOffset), radius, gradient, numSegments);
}
public static void AddCone(this AxDrawList drawList, IGameObject target, float radius, float angle, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddCone(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddConeFilled(this AxDrawList drawList, IGameObject target, float radius, float angle, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint? colorEnd = null, uint numSegments = 0u)
{
drawList.AddConeFilled(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, color, colorEnd, numSegments);
}
public static void AddConeFilled<T>(this AxDrawList drawList, IGameObject target, float radius, float angle, T gradient, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u) where T : IPctGradient
{
drawList.AddConeFilled(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, gradient, numSegments);
}
public static void AddFan(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
float rotation = target.Rotation;
drawList.AddFan(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddFanFilled(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u)
{
float rotation = target.Rotation;
drawList.AddFanFilled(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, color, colorEnd, numSegments);
}
public static void AddFanFilled<T>(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u) where T : IPctGradient
{
float rotation = target.Rotation;
drawList.AddFanFilled(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, gradient, numSegments);
}
public static void AddArc(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
float rotation = target.Rotation;
drawList.AddArc(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddArcFilled(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u)
{
float rotation = target.Rotation;
drawList.AddArcFilled(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, color, colorEnd, numSegments);
}
public static void AddArcFilled<T>(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u) where T : IPctGradient
{
float rotation = target.Rotation;
drawList.AddArcFilled(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, gradient, numSegments);
}
public static void AddRegularPolygon(this AxDrawList drawList, IGameObject target, float radius, uint numSides, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddRegularPolygon(ResolvePosition(target, localOffset), radius, numSides, color, target.Rotation + rotationOffset, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddRegularPolygonFilled(this AxDrawList drawList, IGameObject target, float radius, uint numSides, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f)
{
drawList.AddRegularPolygonFilled(ResolvePosition(target, localOffset), radius, numSides, color, target.Rotation + rotationOffset);
}
public static void AddRegularPolygonFilled<T>(this AxDrawList drawList, IGameObject target, float radius, uint numSides, T gradient, Vector3 localOffset = default(Vector3), float rotationOffset = 0f) where T : IPctGradient
{
drawList.AddRegularPolygonFilled(ResolvePosition(target, localOffset), radius, numSides, gradient, target.Rotation + rotationOffset);
}
public static void AddStar(this AxDrawList drawList, IGameObject target, float outerRadius, float innerRadius, uint numPoints, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddStar(ResolvePosition(target, localOffset), outerRadius, innerRadius, numPoints, color, target.Rotation + rotationOffset, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddStarFilled(this AxDrawList drawList, IGameObject target, float outerRadius, float innerRadius, uint numPoints, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f)
{
drawList.AddStarFilled(ResolvePosition(target, localOffset), outerRadius, innerRadius, numPoints, color, target.Rotation + rotationOffset);
}
public static void AddStarFilled<T>(this AxDrawList drawList, IGameObject target, float outerRadius, float innerRadius, uint numPoints, T gradient, Vector3 localOffset = default(Vector3), float rotationOffset = 0f) where T : IPctGradient
{
drawList.AddStarFilled(ResolvePosition(target, localOffset), outerRadius, innerRadius, numPoints, gradient, target.Rotation + rotationOffset);
}
public static void AddRoundedRect(this AxDrawList drawList, IGameObject target, float halfWidth, float halfHeight, float cornerRadius, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddRoundedRect(ResolvePosition(target, localOffset), halfWidth, halfHeight, cornerRadius, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, target.Rotation + rotationOffset);
}
public static void AddRoundedRectFilled(this AxDrawList drawList, IGameObject target, float halfWidth, float halfHeight, float cornerRadius, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u)
{
drawList.AddRoundedRectFilled(ResolvePosition(target, localOffset), halfWidth, halfHeight, cornerRadius, color, numSegments, target.Rotation + rotationOffset);
}
public static void AddRoundedRectFilled<T>(this AxDrawList drawList, IGameObject target, float halfWidth, float halfHeight, float cornerRadius, T gradient, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u) where T : IPctGradient
{
drawList.AddRoundedRectFilled(ResolvePosition(target, localOffset), halfWidth, halfHeight, cornerRadius, gradient, numSegments, target.Rotation + rotationOffset);
}
public static void AddLine(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddLine(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddLineFilled(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint? colorEnd = null)
{
drawList.AddLineFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, colorEnd);
}
public static void AddLineFilled<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3)) where T : IPctGradient
{
drawList.AddLineFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient);
}
public static void AddArrow(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddArrow(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, headLength, headHalfWidth, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddArrowFilled(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f)
{
drawList.AddArrowFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, headLength, headHalfWidth);
}
public static void AddArrowFilled<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f) where T : IPctGradient
{
drawList.AddArrowFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient, headLength, headHalfWidth);
}
public static void AddPill(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddPill(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddPillFilled(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u)
{
drawList.AddPillFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, numSegments);
}
public static void AddPillFilled<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u) where T : IPctGradient
{
drawList.AddPillFilled(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient, numSegments);
}
public static void AddCircleFilledOnTerrain(this AxDrawList drawList, IGameObject target, float radius, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddCircleFilledOnTerrain(ResolvePosition(target, localOffset), radius, color, colorEnd, numSegments, resolution, yOffset);
}
public static void AddCircleFilledOnTerrain<T>(this AxDrawList drawList, IGameObject target, float radius, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
drawList.AddCircleFilledOnTerrain(ResolvePosition(target, localOffset), radius, gradient, numSegments, resolution, yOffset);
}
public static void AddCircleOnTerrain(this AxDrawList drawList, IGameObject target, float radius, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddCircleOnTerrain(ResolvePosition(target, localOffset), radius, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddConeFilledOnTerrain(this AxDrawList drawList, IGameObject target, float radius, float angle, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint? colorEnd = null, uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddConeFilledOnTerrain(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, color, colorEnd, numSegments, resolution, yOffset);
}
public static void AddConeFilledOnTerrain<T>(this AxDrawList drawList, IGameObject target, float radius, float angle, T gradient, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
drawList.AddConeFilledOnTerrain(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, gradient, numSegments, resolution, yOffset);
}
public static void AddConeOnTerrain(this AxDrawList drawList, IGameObject target, float radius, float angle, uint color, Vector3 localOffset = default(Vector3), float rotationOffset = 0f, uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddConeOnTerrain(ResolvePosition(target, localOffset), radius, target.Rotation + rotationOffset, angle, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddFanFilledOnTerrain(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f)
{
float rotation = target.Rotation;
drawList.AddFanFilledOnTerrain(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, color, colorEnd, numSegments, resolution, yOffset);
}
public static void AddFanFilledOnTerrain<T>(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
float rotation = target.Rotation;
drawList.AddFanFilledOnTerrain(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, gradient, numSegments, resolution, yOffset);
}
public static void AddFanOnTerrain(this AxDrawList drawList, IGameObject target, float innerRadius, float outerRadius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
float rotation = target.Rotation;
drawList.AddFanOnTerrain(ResolvePosition(target, localOffset), innerRadius, outerRadius, rotation + minAngleOffset, rotation + maxAngleOffset, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddLineFilledOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddLineFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, colorEnd, resolution, yOffset);
}
public static void AddLineFilledOnTerrain<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
drawList.AddLineFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient, resolution, yOffset);
}
public static void AddLineOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddLineOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddArrowFilledOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddArrowFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, headLength, headHalfWidth, resolution, yOffset);
}
public static void AddArrowFilledOnTerrain<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
drawList.AddArrowFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient, headLength, headHalfWidth, resolution, yOffset);
}
public static void AddArrowOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), float headLength = 0f, float headHalfWidth = 0f, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddArrowOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, headLength, headHalfWidth, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddPillFilledOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddPillFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, numSegments, resolution, yOffset);
}
public static void AddPillFilledOnTerrain<T>(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, T gradient, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
drawList.AddPillFilledOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, gradient, numSegments, resolution, yOffset);
}
public static void AddPillOnTerrain(this AxDrawList drawList, IGameObject from, IGameObject to, float halfWidth, uint color, Vector3 fromOffset = default(Vector3), Vector3 toOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
drawList.AddPillOnTerrain(ResolvePosition(from, fromOffset), ResolvePosition(to, toOffset), halfWidth, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddArcFilledOnTerrain(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint? colorEnd = null, uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f)
{
float rotation = target.Rotation;
drawList.AddArcFilledOnTerrain(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, color, colorEnd, numSegments, resolution, yOffset);
}
public static void AddArcFilledOnTerrain<T>(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, T gradient, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float resolution = 0.5f, float yOffset = 0.02f) where T : IPctGradient
{
float rotation = target.Rotation;
drawList.AddArcFilledOnTerrain(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, gradient, numSegments, resolution, yOffset);
}
public static void AddArcOnTerrain(this AxDrawList drawList, IGameObject target, float radius, float minAngleOffset, float maxAngleOffset, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null, float resolution = 0.5f, float yOffset = 0.02f)
{
float rotation = target.Rotation;
drawList.AddArcOnTerrain(ResolvePosition(target, localOffset), radius, rotation + minAngleOffset, rotation + maxAngleOffset, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd, resolution, yOffset);
}
public static void AddWireframeBox(this AxDrawList drawList, IGameObject target, Vector3 halfExtent, uint color, Vector3 localOffset = default(Vector3), float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddWireframeBox(ResolvePosition(target, localOffset), halfExtent, color, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddWireframeSphere(this AxDrawList drawList, IGameObject target, float radius, uint color, Vector3 localOffset = default(Vector3), uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddWireframeSphere(ResolvePosition(target, localOffset), radius, color, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
public static void AddWireframeCylinder(this AxDrawList drawList, IGameObject target, float radius, float halfHeight, uint color, Vector3 localOffset = default(Vector3), uint numVerticals = 4u, uint numSegments = 0u, float thickness = 2f, float dashLength = 0f, float gapLength = 0f, float dashOffset = 0f, uint? colorEnd = null)
{
drawList.AddWireframeCylinder(ResolvePosition(target, localOffset), radius, halfHeight, color, numVerticals, numSegments, thickness, dashLength, gapLength, dashOffset, colorEnd);
}
}

View file

@ -0,0 +1,20 @@
namespace Auspex;
public record struct AxDxParams
{
public float OccludedAlpha { get; init; }
public float OcclusionTolerance { get; init; }
public float FadeStart { get; init; }
public float FadeStop { get; init; }
public AxDxParams()
{
OccludedAlpha = 1f;
OcclusionTolerance = 0f;
FadeStart = float.PositiveInfinity;
FadeStop = float.PositiveInfinity;
}
}

27
Auspex/Auspex/AxEasing.cs Normal file
View file

@ -0,0 +1,27 @@
namespace Auspex;
public enum AxEasing
{
Linear,
EaseIn,
EaseOut,
EaseInOut,
CubicIn,
CubicOut,
CubicInOut,
SineIn,
SineOut,
SineInOut,
ExponentialIn,
ExponentialOut,
ExponentialInOut,
BackIn,
BackOut,
BackInOut,
ElasticIn,
ElasticOut,
ElasticInOut,
BounceIn,
BounceOut,
BounceInOut
}

View file

@ -0,0 +1,8 @@
namespace Auspex;
public enum AxFalloffType : uint
{
Linear,
Quadratic,
Cubic
}

View file

@ -0,0 +1,3 @@
namespace Auspex;
public readonly record struct AxGradientStop(float Position, uint Color);

View file

@ -0,0 +1,48 @@
using System.Numerics;
namespace Auspex;
public struct AxLightConfig
{
public static readonly AxLightConfig Default = new AxLightConfig();
public Vector3 Color;
public float Intensity;
public float Range;
public float LightAngle;
public float FalloffAngle;
public AxLightFlags Flags;
public Quaternion? Rotation;
public AxFalloffType FalloffType;
public float FalloffPower;
public float ShadowNear;
public float ShadowFar;
public float CharaShadowRange;
public AxLightConfig()
{
Color = new Vector3(20f, 20f, 20f);
Intensity = 1f;
Range = 35f;
LightAngle = 45f;
FalloffAngle = 0.5f;
Flags = AxLightFlags.Reflection;
Rotation = null;
FalloffType = AxFalloffType.Quadratic;
FalloffPower = 1f;
ShadowNear = 0.01f;
ShadowFar = 17f;
CharaShadowRange = 110f;
}
}

View file

@ -0,0 +1,13 @@
using System;
namespace Auspex;
[Flags]
public enum AxLightFlags : uint
{
None = 0u,
Reflection = 1u,
DynamicShadow = 2u,
CharaShadow = 4u,
ObjectShadow = 8u
}

View file

@ -0,0 +1,9 @@
namespace Auspex;
public enum AxLightType : uint
{
Directional = 1u,
PointLight,
SpotLight,
AreaLight
}

View file

@ -0,0 +1,8 @@
namespace Auspex;
public enum AxLineCap
{
Butt,
Square,
Round
}

View file

@ -0,0 +1,9 @@
namespace Auspex;
public enum AxLineJoin
{
None,
Miter,
Bevel,
Round
}

View file

@ -0,0 +1,33 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxLinearGradient : IPctGradient
{
private readonly Vector3 _start;
private readonly Vector3 _axis;
private readonly float _invLenSq;
private readonly Vector4 _colorStart;
private readonly Vector4 _colorEnd;
public AxLinearGradient(Vector3 start, Vector3 end, uint colorStart, uint colorEnd)
{
_start = start;
_axis = end - start;
_invLenSq = 1f / Vector3.Dot(_axis, _axis);
_colorStart = colorStart.ToVector4();
_colorEnd = colorEnd.ToVector4();
}
public uint ColorAt(Vector3 point)
{
float amount = Math.Clamp(Vector3.Dot(point - _start, _axis) * _invLenSq, 0f, 1f);
return Vector4.Lerp(_colorStart, _colorEnd, amount).ToUint();
}
}

184
Auspex/Auspex/AxMinimap.cs Normal file
View file

@ -0,0 +1,184 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using Dalamud.Bindings.ImGui;
namespace Auspex;
public readonly record struct AxMinimap(Vector2 screenCenter, float screenHalfSize, Vector3 viewCenter, float viewRadius, float cameraAzimuth)
{
private readonly float _cos = MathF.Cos(cameraAzimuth);
private readonly float _sin = MathF.Sin(cameraAzimuth);
private readonly float _scale = screenHalfSize / viewRadius;
public Vector2 WorldToScreen(Vector3 world)
{
return WorldToScreen(world.X, world.Z);
}
public Vector2 WorldToScreen(float worldX, float worldZ)
{
float num = worldX - viewCenter.X;
float num2 = worldZ - viewCenter.Z;
float num3 = num * _cos - num2 * _sin;
float num4 = num * _sin + num2 * _cos;
return new Vector2(screenCenter.X + num3 * _scale, screenCenter.Y + num4 * _scale);
}
public void PushClip(ImDrawListPtr drawList)
{
Vector2 clipRectMin = screenCenter - new Vector2(screenHalfSize);
Vector2 clipRectMax = screenCenter + new Vector2(screenHalfSize);
drawList.PushClipRect(clipRectMin, clipRectMax, intersectWithCurrentClipRect: true);
}
public void PopClip(ImDrawListPtr drawList)
{
drawList.PopClipRect();
}
public void DrawTerrain(ImDrawListPtr drawList, AxTerrainMap map, uint voidColor, uint terrainColor)
{
DrawTerrainBackground(drawList, voidColor);
if (!map.IsEmpty)
{
DrawTerrainCells(drawList, map, (float _) => terrainColor);
}
}
public void DrawTerrain(ImDrawListPtr drawList, AxTerrainMap map, uint voidColor, Func<float, uint> colorFunc)
{
DrawTerrainBackground(drawList, voidColor);
if (!map.IsEmpty)
{
DrawTerrainCells(drawList, map, colorFunc);
}
}
private void DrawTerrainBackground(ImDrawListPtr drawList, uint voidColor)
{
Vector2 pMin = screenCenter - new Vector2(screenHalfSize);
Vector2 pMax = screenCenter + new Vector2(screenHalfSize);
drawList.AddRectFilled(pMin, pMax, voidColor);
}
private void DrawTerrainCells(ImDrawListPtr drawList, AxTerrainMap map, Func<float, uint> colorFunc)
{
float cellSize = map.CellSize;
float num = viewCenter.X - viewRadius;
float num2 = viewCenter.Z - viewRadius;
float num3 = viewCenter.X + viewRadius;
float num4 = viewCenter.Z + viewRadius;
int num5 = Math.Max(0, (int)MathF.Floor((num - map.WorldMinX) / cellSize));
int num6 = Math.Max(0, (int)MathF.Floor((num2 - map.WorldMinZ) / cellSize));
int num7 = Math.Min(map.GridWidth - 1, (int)MathF.Floor((num3 - map.WorldMinX) / cellSize));
int num8 = Math.Min(map.GridHeight - 1, (int)MathF.Floor((num4 - map.WorldMinZ) / cellSize));
float num9 = 1f / _scale;
for (int i = num6; i <= num8; i++)
{
float num10 = map.WorldMinZ + (float)i * cellSize;
float worldZ = num10 + cellSize + num9;
for (int j = num5; j <= num7; j++)
{
float height = map.GetHeight(j, i);
if (!float.IsNaN(height))
{
float num11 = map.WorldMinX + (float)j * cellSize;
float worldX = num11 + cellSize + num9;
Vector2 p = WorldToScreen(num11, num10);
Vector2 p2 = WorldToScreen(worldX, num10);
Vector2 p3 = WorldToScreen(worldX, worldZ);
Vector2 p4 = WorldToScreen(num11, worldZ);
uint col = colorFunc(height);
drawList.AddQuadFilled(p, p2, p3, p4, col);
}
}
}
}
public void AddCircle(ImDrawListPtr drawList, Vector3 worldPos, float worldRadius, uint color, float thickness = 1f, int numSegments = 0)
{
Vector2 center = WorldToScreen(worldPos);
float radius = worldRadius * _scale;
drawList.AddCircle(center, radius, color, numSegments, thickness);
}
public void AddCircleFilled(ImDrawListPtr drawList, Vector3 worldPos, float worldRadius, uint color, int numSegments = 0)
{
Vector2 center = WorldToScreen(worldPos);
float radius = worldRadius * _scale;
drawList.AddCircleFilled(center, radius, color, numSegments);
}
public void AddLine(ImDrawListPtr drawList, Vector3 worldStart, Vector3 worldEnd, uint color, float thickness = 1f)
{
Vector2 p = WorldToScreen(worldStart);
Vector2 p2 = WorldToScreen(worldEnd);
drawList.AddLine(p, p2, color, thickness);
}
public void AddArrow(ImDrawListPtr drawList, Vector3 worldStart, Vector3 worldEnd, uint color, float thickness = 1f, float headLength = 8f)
{
Vector2 vector = WorldToScreen(worldStart);
Vector2 vector2 = WorldToScreen(worldEnd);
drawList.AddLine(vector, vector2, color, thickness);
Vector2 vector3 = vector2 - vector;
float num = vector3.Length();
if (!(num < 0.001f))
{
vector3 /= num;
Vector2 vector4 = new Vector2(0f - vector3.Y, vector3.X);
float num2 = MathF.Min(headLength, num * 0.4f);
float num3 = num2 * 0.5f;
Vector2 p = vector2;
Vector2 p2 = vector2 - vector3 * num2 + vector4 * num3;
Vector2 p3 = vector2 - vector3 * num2 - vector4 * num3;
drawList.AddTriangleFilled(p, p2, p3, color);
}
}
public void AddTriangleMarker(ImDrawListPtr drawList, Vector3 worldPos, float heading, uint color, float sizePixels = 10f)
{
Vector2 vector = WorldToScreen(worldPos);
float num = heading - cameraAzimuth;
float num2 = MathF.Cos(num);
float num3 = MathF.Sin(num);
float num4 = sizePixels * 0.6f;
Vector2 p = vector + new Vector2((0f - num3) * sizePixels * 0.6f, (0f - num2) * sizePixels * 0.6f);
Vector2 p2 = vector + new Vector2((0f - num3) * ((0f - sizePixels) * 0.4f) - num2 * num4 * 0.5f, (0f - num2) * ((0f - sizePixels) * 0.4f) + num3 * num4 * 0.5f);
Vector2 p3 = vector + new Vector2((0f - num3) * ((0f - sizePixels) * 0.4f) + num2 * num4 * 0.5f, (0f - num2) * ((0f - sizePixels) * 0.4f) - num3 * num4 * 0.5f);
drawList.AddTriangleFilled(p, p2, p3, color);
}
public void AddText(ImDrawListPtr drawList, Vector3 worldPos, uint color, string text)
{
Vector2 pos = WorldToScreen(worldPos);
drawList.AddText(pos, color, text);
}
public void DrawBorder(ImDrawListPtr drawList, uint color, float thickness = 1f)
{
Vector2 pMin = screenCenter - new Vector2(screenHalfSize);
Vector2 pMax = screenCenter + new Vector2(screenHalfSize);
drawList.AddRect(pMin, pMax, color, 0f, ImDrawFlags.None, thickness);
}
[CompilerGenerated]
private bool PrintMembers(StringBuilder builder)
{
builder.Append("screenCenter = ");
builder.Append(screenCenter.ToString());
builder.Append(", screenHalfSize = ");
builder.Append(screenHalfSize.ToString());
builder.Append(", viewCenter = ");
builder.Append(viewCenter.ToString());
builder.Append(", viewRadius = ");
builder.Append(viewRadius.ToString());
builder.Append(", cameraAzimuth = ");
builder.Append(cameraAzimuth.ToString());
return true;
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxMultiStopAngularGradient : IPctGradient
{
private readonly Vector3 _center;
private readonly float _minAngle;
private readonly float _invRange;
private readonly float[] _positions;
private readonly Vector4[] _colors;
public AxMultiStopAngularGradient(Vector3 center, float minAngle, float maxAngle, params AxGradientStop[] stops)
{
_center = center;
_minAngle = minAngle;
_invRange = 1f / (maxAngle - minAngle);
(_positions, _colors) = AxMultiStopLinearGradient.ParseStops(stops);
}
public uint ColorAt(Vector3 point)
{
return AxMultiStopLinearGradient.SampleStops(Math.Clamp((MathF.Atan2(point.Z - _center.Z, point.X - _center.X) - (float)Math.PI / 2f - _minAngle) * _invRange % 1f, 0f, 1f), _positions, _colors).ToUint();
}
}

View file

@ -0,0 +1,70 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxMultiStopLinearGradient : IPctGradient
{
private readonly Vector3 _start;
private readonly Vector3 _axis;
private readonly float _invLenSq;
private readonly float[] _positions;
private readonly Vector4[] _colors;
public AxMultiStopLinearGradient(Vector3 start, Vector3 end, params AxGradientStop[] stops)
{
_start = start;
_axis = end - start;
_invLenSq = 1f / Vector3.Dot(_axis, _axis);
(_positions, _colors) = ParseStops(stops);
}
public uint ColorAt(Vector3 point)
{
return SampleStops(Math.Clamp(Vector3.Dot(point - _start, _axis) * _invLenSq, 0f, 1f), _positions, _colors).ToUint();
}
internal static (float[] Positions, Vector4[] Colors) ParseStops(AxGradientStop[] stops)
{
AxGradientStop[] array = (AxGradientStop[])stops.Clone();
Array.Sort(array, (AxGradientStop a, AxGradientStop b) => a.Position.CompareTo(b.Position));
float[] array2 = new float[array.Length];
Vector4[] array3 = new Vector4[array.Length];
for (int num = 0; num < array.Length; num++)
{
array2[num] = array[num].Position;
array3[num] = array[num].Color.ToVector4();
}
return (Positions: array2, Colors: array3);
}
internal static Vector4 SampleStops(float t, float[] positions, Vector4[] colors)
{
if (colors.Length == 0)
{
return Vector4.Zero;
}
if (t <= positions[0])
{
return colors[0];
}
if (t >= positions[^1])
{
return colors[^1];
}
for (int i = 0; i < positions.Length - 1; i++)
{
if (t <= positions[i + 1])
{
float amount = (t - positions[i]) / (positions[i + 1] - positions[i]);
return Vector4.Lerp(colors[i], colors[i + 1], amount);
}
}
return colors[^1];
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxMultiStopRadialGradient : IPctGradient
{
private readonly Vector3 _center;
private readonly float _invRadius;
private readonly float[] _positions;
private readonly Vector4[] _colors;
public AxMultiStopRadialGradient(Vector3 center, float radius, params AxGradientStop[] stops)
{
_center = center;
_invRadius = 1f / radius;
(_positions, _colors) = AxMultiStopLinearGradient.ParseStops(stops);
}
public uint ColorAt(Vector3 point)
{
return AxMultiStopLinearGradient.SampleStops(Math.Clamp(Vector3.Distance(point, _center) * _invRadius, 0f, 1f), _positions, _colors).ToUint();
}
}

View file

@ -0,0 +1,30 @@
using System;
using System.Numerics;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public readonly struct AxRadialGradient : IPctGradient
{
private readonly Vector3 _center;
private readonly float _invRadius;
private readonly Vector4 _colorInner;
private readonly Vector4 _colorOuter;
public AxRadialGradient(Vector3 center, float radius, uint colorInner, uint colorOuter)
{
_center = center;
_invRadius = 1f / radius;
_colorInner = colorInner.ToVector4();
_colorOuter = colorOuter.ToVector4();
}
public uint ColorAt(Vector3 point)
{
float amount = Math.Clamp(Vector3.Distance(point, _center) * _invRadius, 0f, 1f);
return Vector4.Lerp(_colorInner, _colorOuter, amount).ToUint();
}
}

View file

@ -0,0 +1,8 @@
namespace Auspex;
public enum AxRepeatMode
{
None,
Repeat,
PingPong
}

View file

@ -0,0 +1,10 @@
using System;
namespace Auspex;
[Flags]
public enum AxStrokeFlags
{
None = 0,
Closed = 1
}

View file

@ -0,0 +1,124 @@
using System;
using System.Numerics;
namespace Auspex;
public sealed class AxTerrainMap
{
private readonly float[] _heights;
public float WorldMinX { get; }
public float WorldMinZ { get; }
public int GridWidth { get; }
public int GridHeight { get; }
public float CellSize { get; }
public bool IsEmpty { get; }
private AxTerrainMap(float[] heights, float worldMinX, float worldMinZ, int gridWidth, int gridHeight, float cellSize, bool isEmpty)
{
_heights = heights;
WorldMinX = worldMinX;
WorldMinZ = worldMinZ;
GridWidth = gridWidth;
GridHeight = gridHeight;
CellSize = cellSize;
IsEmpty = isEmpty;
}
public static AxTerrainMap Generate(Vector3 center, float halfExtentX, float halfExtentZ, float cellSize = 1f)
{
float num = center.X - halfExtentX;
float num2 = center.Z - halfExtentZ;
int num3 = (int)MathF.Ceiling(halfExtentX * 2f / cellSize);
int num4 = (int)MathF.Ceiling(halfExtentZ * 2f / cellSize);
if (num3 <= 0 || num4 <= 0)
{
return new AxTerrainMap(Array.Empty<float>(), 0f, 0f, 0, 0, cellSize, isEmpty: true);
}
float[] array = new float[num3 * num4];
int num5 = num3;
int num6 = num4;
int num7 = -1;
int num8 = -1;
for (int i = 0; i < num4; i++)
{
float z = num2 + ((float)i + 0.5f) * cellSize;
for (int j = 0; j < num3; j++)
{
if (!float.IsNaN(array[i * num3 + j] = AxTerrainQuery.GetTerrainYRaw(num + ((float)j + 0.5f) * cellSize, z)))
{
if (j < num5)
{
num5 = j;
}
if (j > num7)
{
num7 = j;
}
if (i < num6)
{
num6 = i;
}
if (i > num8)
{
num8 = i;
}
}
}
}
if (num7 < 0)
{
return new AxTerrainMap(Array.Empty<float>(), 0f, 0f, 0, 0, cellSize, isEmpty: true);
}
int num9 = num7 - num5 + 1;
int num10 = num8 - num6 + 1;
float[] array2 = new float[num9 * num10];
for (int k = 0; k < num10; k++)
{
int sourceIndex = (k + num6) * num3 + num5;
Array.Copy(array, sourceIndex, array2, k * num9, num9);
}
float worldMinX = num + (float)num5 * cellSize;
float worldMinZ = num2 + (float)num6 * cellSize;
return new AxTerrainMap(array2, worldMinX, worldMinZ, num9, num10, cellSize, isEmpty: false);
}
public float GetHeight(int cellX, int cellZ)
{
if ((uint)cellX >= (uint)GridWidth || (uint)cellZ >= (uint)GridHeight)
{
return float.NaN;
}
return _heights[cellZ * GridWidth + cellX];
}
public bool IsWalkable(int cellX, int cellZ)
{
return !float.IsNaN(GetHeight(cellX, cellZ));
}
public bool IsWalkable(float worldX, float worldZ)
{
var (cellX, cellZ) = WorldToCell(worldX, worldZ);
return IsWalkable(cellX, cellZ);
}
public (int cellX, int cellZ) WorldToCell(float worldX, float worldZ)
{
int item = (int)MathF.Floor((worldX - WorldMinX) / CellSize);
int item2 = (int)MathF.Floor((worldZ - WorldMinZ) / CellSize);
return (cellX: item, cellZ: item2);
}
public (float worldX, float worldZ) CellToWorld(int cellX, int cellZ)
{
float item = WorldMinX + ((float)cellX + 0.5f) * CellSize;
float item2 = WorldMinZ + ((float)cellZ + 0.5f) * CellSize;
return (worldX: item, worldZ: item2);
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
namespace Auspex;
public static class AxTerrainQuery
{
private static readonly Dictionary<(int, int), float> _cache = new Dictionary<(int, int), float>();
public static float GetTerrainY(float x, float z, float fallbackY = 0f)
{
float terrainYRaw = GetTerrainYRaw(x, z);
if (!float.IsNaN(terrainYRaw))
{
return terrainYRaw;
}
return fallbackY;
}
public static float GetTerrainYRaw(float x, float z)
{
int item = (int)MathF.Round(x * 4f);
int item2 = (int)MathF.Round(z * 4f);
if (_cache.TryGetValue((item, item2), out var value))
{
return value;
}
Vector3 origin = new Vector3(x, 1000f, z);
Vector3 direction = new Vector3(0f, -1f, 0f);
RaycastHit hitInfo;
float num = ((!BGCollisionModule.RaycastMaterialFilter(origin, direction, out hitInfo, 2000f)) ? float.NaN : hitInfo.Point.Y);
_cache[(item, item2)] = num;
return num;
}
public static void ClearCache()
{
_cache.Clear();
}
}

View file

@ -0,0 +1,39 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using Dalamud.Bindings.ImGui;
namespace Auspex;
public record struct AxTexture
{
public Vector2 Size => new Vector2(Width, Height);
public ImTextureID TextureId;
public uint Width;
public uint Height;
public AxTexture(IntPtr textureId, uint width, uint height)
{
TextureId = (ulong)(nint)textureId;
Width = width;
Height = height;
}
[CompilerGenerated]
private bool PrintMembers(StringBuilder builder)
{
builder.Append("TextureId = ");
builder.Append(TextureId.ToString());
builder.Append(", Width = ");
builder.Append(Width.ToString());
builder.Append(", Height = ");
builder.Append(Height.ToString());
builder.Append(", Size = ");
builder.Append(Size.ToString());
return true;
}
}

23
Auspex/Auspex/CImGui.cs Normal file
View file

@ -0,0 +1,23 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Auspex;
internal static class CImGui
{
[DllImport("cimgui", ExactSpelling = true)]
[LibraryImport("cimgui")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static extern void igBringWindowToDisplayFront(IntPtr ptr);
[DllImport("cimgui", ExactSpelling = true)]
[LibraryImport("cimgui")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static extern void igBringWindowToDisplayBack(IntPtr ptr);
[DllImport("cimgui", ExactSpelling = true)]
[LibraryImport("cimgui")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })]
internal static extern IntPtr igGetCurrentWindow();
}

View file

@ -0,0 +1,8 @@
using System.Numerics;
namespace Auspex;
public interface IPctGradient
{
uint ColorAt(Vector3 point);
}

View file

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Auspex.Lighting.Internal;
using Auspex.Vfx.Internal;
namespace Auspex;
public sealed class LightRenderer : IDisposable
{
private static readonly Dictionary<AxLightType, string> LightTypeNames = Enum.GetValues<AxLightType>().ToDictionary((AxLightType v) => v, (AxLightType v) => v.ToString());
private FrameLifetimeTracker<NativeLightHandle> trackedLights;
public LightRenderer()
{
trackedLights = new FrameLifetimeTracker<NativeLightHandle>();
}
public static Vector3 LinearToGameHDR(Vector3 linearRgb)
{
linearRgb *= 4f;
return linearRgb * linearRgb;
}
public static Vector3 GameHDRToLinear(Vector3 gameHdr)
{
return Vector3.SquareRoot(gameHdr) / 4f;
}
public void AddDirectionalLight(string id, Vector3 position, AxLightConfig? config = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
CreateOrUpdateLight(id, AxLightType.Directional, position, config ?? AxLightConfig.Default);
}
public void AddPointLight(string id, Vector3 position, AxLightConfig? config = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
CreateOrUpdateLight(id, AxLightType.PointLight, position, config ?? AxLightConfig.Default);
}
public void AddSpotLight(string id, Vector3 position, AxLightConfig? config = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
CreateOrUpdateLight(id, AxLightType.SpotLight, position, config ?? AxLightConfig.Default);
}
public void AddAreaLight(string id, Vector3 position, AxLightConfig? config = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
CreateOrUpdateLight(id, AxLightType.AreaLight, position, config ?? AxLightConfig.Default);
}
private void CreateOrUpdateLight(string id, AxLightType lightType, Vector3 position, AxLightConfig config)
{
(string, string) key = (id, LightTypeNames[lightType]);
if (trackedLights.IsTouched(key))
{
return;
}
if (trackedLights.TryTouchExisting(key, out NativeLightHandle o))
{
o.UpdatePosition(position);
if (config.Rotation.HasValue)
{
o.UpdateRotation(config.Rotation.Value);
}
o.UpdateColor(config.Color);
o.UpdateIntensity(config.Intensity);
o.UpdateRange(config.Range);
o.UpdateAngles(config.LightAngle, config.FalloffAngle);
o.UpdateFlags(config.Flags);
o.UpdateFalloff(config.FalloffType, config.FalloffPower);
o.UpdateShadow(config.ShadowNear, config.ShadowFar, config.CharaShadowRange);
}
else
{
trackedLights.TouchNew(key, NativeLightHandle.Create(position, lightType, config));
}
}
public bool RemoveLight(string id, AxLightType lightType)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
return trackedLights.Remove((id, LightTypeNames[lightType]));
}
internal void Update()
{
trackedLights.Update();
trackedLights.ForEachActive(delegate(NativeLightHandle light)
{
light.Tick();
});
}
public void Dispose()
{
trackedLights.Dispose();
GC.SuppressFinalize(this);
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Runtime.InteropServices;
namespace Auspex;
internal static class SigScanHelper
{
internal static T Scan<T>(string sig, string name) where T : Delegate
{
try
{
return Marshal.GetDelegateForFunctionPointer<T>(AuspexService.SigScanner.ScanText(sig));
}
catch (Exception innerException)
{
throw new InvalidOperationException("[Auspex] Failed to scan signature for " + name + ": " + sig, innerException);
}
}
}

View file

@ -0,0 +1,122 @@
using System;
using System.Numerics;
using Auspex.Vfx.Internal;
using Dalamud.Game.ClientState.Objects.Types;
namespace Auspex;
public sealed class VfxRenderer : IDisposable
{
private static readonly Vector4 White = Vector4.One;
private FrameLifetimeTracker<VfxInstance> trackedVfx;
private FrameLifetimeTracker<ActorBoundVfx> trackedGOVfx;
public static string OmenPath(string name)
{
return "vfx/omen/eff/" + name + ".avfx";
}
public static string LockonPath(string name)
{
return "vfx/lockon/eff/" + name + ".avfx";
}
public static string ChannelingPath(string name)
{
return "vfx/channeling/eff/" + name + ".avfx";
}
public static string CommonPath(string name)
{
return "vfx/common/eff/" + name + ".avfx";
}
public VfxRenderer()
{
trackedVfx = new FrameLifetimeTracker<VfxInstance>();
trackedGOVfx = new FrameLifetimeTracker<ActorBoundVfx>();
}
public void AddOmen(string id, string name, Vector3 origin, Vector3? scale = null, float rotation = 0f, Vector4? color = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
ArgumentNullException.ThrowIfNull((object)name, "name");
CreateOrUpdateVfx(id, OmenPath(name), origin, scale ?? Vector3.One, rotation, color ?? White);
}
public void AddLockon(string id, string name, IGameObject target, Vector3? scale = null, Vector4? color = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
ArgumentNullException.ThrowIfNull((object)name, "name");
CreateOrUpdateVfx(id, LockonPath(name), target, target, scale ?? Vector3.One, color ?? White);
}
public void AddChanneling(string id, string name, IGameObject target, IGameObject source, Vector3? scale = null, Vector4? color = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
ArgumentNullException.ThrowIfNull((object)name, "name");
CreateOrUpdateVfx(id, ChannelingPath(name), target, source, scale ?? Vector3.One, color ?? White);
}
public void AddCommon(string id, string name, IGameObject target, Vector3? scale = null, Vector4? color = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
ArgumentNullException.ThrowIfNull((object)name, "name");
CreateOrUpdateVfx(id, CommonPath(name), target, target, scale ?? Vector3.One, color ?? White);
}
public void AddCustom(string id, string path, Vector3 origin, Vector3? scale = null, float rotation = 0f, Vector4? color = null)
{
ArgumentNullException.ThrowIfNull((object)id, "id");
ArgumentNullException.ThrowIfNull((object)path, "path");
CreateOrUpdateVfx(id, path, origin, scale ?? Vector3.One, rotation, color ?? White);
}
private void CreateOrUpdateVfx(string id, string path, Vector3 position, Vector3 scale, float rotation, Vector4 color)
{
(string, string) key = (id, path);
if (!trackedVfx.IsTouched(key))
{
if (trackedVfx.TryTouchExisting(key, out VfxInstance o))
{
o.UpdateTransform(position, scale, rotation);
o.UpdateColor(color);
}
else
{
trackedVfx.TouchNew(key, VfxInstance.Create(path, position, scale, rotation, color));
}
}
}
private void CreateOrUpdateVfx(string id, string path, IGameObject target, IGameObject source, Vector3 scale, Vector4 color)
{
(string, string) key = (id, path);
if (!trackedGOVfx.IsTouched(key))
{
if (trackedGOVfx.TryTouchExisting(key, out ActorBoundVfx o))
{
o.UpdateColor(color);
}
else
{
trackedGOVfx.TouchNew(key, ActorBoundVfx.Create(path, target, source, scale, color));
}
}
}
internal void Update()
{
trackedVfx.Update();
trackedGOVfx.Update();
}
public void Dispose()
{
trackedVfx.Dispose();
trackedGOVfx.Dispose();
GC.SuppressFinalize(this);
}
}

View file

@ -0,0 +1,193 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Auspex.Rendering.Direct3D;
namespace Auspex;
public static class VfxRendererExtensions
{
private const float DefaultHeight = 7f;
private const string OmenCircle = "general_1bf";
private const string OmenLine = "general02f";
private const string OmenRectangle = "general_x02f";
public static readonly ImmutableList<(string, float)> OmenDonutHoleSizes;
public static string? GetOmenConeForAngle(int angle)
{
return angle switch
{
15 => "gl_fan015_0x",
20 => "gl_fan020_0f",
30 => "gl_fan030_1bf",
45 => "gl_fan045_1bf",
60 => "gl_fan060_1bf",
80 => "gl_fan80_o0g",
90 => "gl_fan090_1bf",
120 => "gl_fan120_1bf",
130 => "gl_fan130_0x",
135 => "gl_fan135_c0g",
150 => "gl_fan150_1bf",
180 => "gl_fan180_1bf",
210 => "gl_fan210_1bf",
225 => "gl_fan225_c0g",
270 => "gl_fan270_0100af",
360 => "general_1bf",
_ => null,
};
}
public static string? GetDonutOmenForRadius(float innerRadius, float outerRadius)
{
float desiredRatio = innerRadius / outerRadius;
return OmenDonutHoleSizes.Where<(string, float)>(delegate((string, float) x)
{
float num = desiredRatio - x.Item2;
return num >= 0f && num < 0.01f;
}).FirstOrDefault().Item1;
}
public static (string, float) GetDonutHoleOmenForRadius(float innerRadius)
{
var (item, num) = OmenDonutHoleSizes[0];
return (item, innerRadius / num);
}
public static void AddLine(this VfxRenderer r, string id, Vector3 start, Vector3 stop, float halfWidth, Vector4? color = null)
{
float rotation = MathF.Atan2(stop.X - start.X, stop.Z - start.Z);
float length = Vector2.Distance(new Vector2(stop.X, stop.Z), new Vector2(start.X, start.Z));
r.AddLine(id, start, length, halfWidth, rotation, color);
}
public static void AddLine(this VfxRenderer r, string id, Vector3 start, float length, float halfWidth, float rotation, Vector4? color = null)
{
r.AddOmen(id, "general02f", start, new Vector3(halfWidth, 7f, length), rotation, color);
}
public static void AddRectangle(this VfxRenderer r, string id, Vector3 origin, float halfWidth, float halfLength, float rotation = 0f, Vector4? color = null)
{
r.AddOmen(id, "general_x02f", origin, new Vector3(halfWidth, 7f, halfLength), rotation, color);
}
public static void AddCircle(this VfxRenderer r, string id, Vector3 origin, float radius, Vector4? color = null)
{
r.AddOmen(id, "general_1bf", origin, new Vector3(radius, 7f, radius), 0f, color);
}
public static bool AddCone(this VfxRenderer r, string id, Vector3 origin, float radius, float rotation, int angleWidth, Vector4? color = null)
{
string omenConeForAngle = GetOmenConeForAngle(angleWidth);
if (omenConeForAngle != null)
{
r.AddOmen(id, omenConeForAngle, origin, new Vector3(radius, 7f, radius), rotation, color);
}
return omenConeForAngle != null;
}
public static void AddDonutHole(this VfxRenderer r, string id, Vector3 origin, float innerRadius, Vector4? color = null)
{
var (name, num) = GetDonutHoleOmenForRadius(innerRadius);
r.AddOmen(id, name, origin, new Vector3(num, 7f, num), 0f, color);
}
public static bool AddDonut(this VfxRenderer r, string id, Vector3 origin, float innerRadius, float outerRadius, Vector4? color = null)
{
string donutOmenForRadius = GetDonutOmenForRadius(innerRadius, outerRadius);
if (donutOmenForRadius != null)
{
r.AddOmen(id, donutOmenForRadius, origin, new Vector3(outerRadius, 7f, outerRadius), 0f, color);
}
return donutOmenForRadius != null;
}
public static bool AddFan(this VfxRenderer r, string id, Vector3 origin, float innerRadius, float outerRadius, float minAngle, float maxAngle, Vector4? color = null)
{
bool flag = (double)(maxAngle - minAngle) >= 6.283184482025146;
if (innerRadius <= 0f)
{
if (flag)
{
r.AddCircle(id, origin, outerRadius, color);
return true;
}
float num = maxAngle - minAngle;
float num2 = minAngle + num / 2f;
return r.AddCone(id, origin, outerRadius, 0f - num2, (int)MathF.Round(num / ((float)Math.PI * 2f) * 360f, 0), color);
}
if (flag)
{
if (innerRadius > 1f && outerRadius > 50f)
{
r.AddDonutHole(id, origin, innerRadius, color);
return true;
}
return r.AddDonut(id, origin, innerRadius, outerRadius, color);
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 AlphaDXToVFX(this uint color)
{
Vector4 result = color.ToVector4();
if (result.W <= 16f / 51f)
{
result.W *= 3.1875f;
}
else
{
float num = result.W - 16f / 51f;
result.W = 1f + num * 1.96f;
}
return result;
}
static VfxRendererExtensions()
{
_003C_003Ey__InlineArray36<(string, float)> buffer = default(_003C_003Ey__InlineArray36<(string, float)>);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 0) = ("gl_sircle_5003bf", 0.07f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 1) = ("gl_sircle_7006x", 0.086f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 2) = ("gl_sircle_1034bf", 0.1138f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 3) = ("gl_sircle_4005bf", 0.126f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 4) = ("gl_circle_5007_x1", 0.14f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 5) = ("gl_sircle_6009at", 1.5f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 6) = ("gl_sircle_6010bf", 0.166f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 7) = ("gl_sircle_1703x", 0.1767f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 8) = ("gl_sircle_2004bv", 0.2f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 9) = ("gl_sircle_7015k1", 0.215f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 10) = ("gl_sircle_3007bx", 0.233f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 11) = ("gl_sircle_2005bf", 0.25f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 12) = ("gl_sircle_1905bf", 0.263f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 13) = ("gl_sircle_3008bf", 0.266f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 14) = ("gl_sircle_1805r1", 0.277f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 15) = ("gl_sircle_4012c", 0.3f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 16) = ("x6r8_b4_donut13m_4_01k1", 0.311f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 17) = ("gl_sircle_2508_o0t1", 0.32f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 18) = ("x6fa_donut01_o0a1", 0.333f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 19) = ("gl_sircle_1505bt1", 0.335f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 20) = ("gl_sircle_1907y0x", 0.37f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 21) = ("gl_sircle_2008bi", 0.4f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 22) = ("gl_sircle_3014bf", 0.466f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 23) = ("gl_sircle_2010bf", 0.5f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 24) = ("gl_sircle_4021x", 0.525f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 25) = ("gl_sircle_2011v", 0.55f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 26) = ("gl_sircle_3520x", 0.572f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 27) = ("gl_sircle_5030c", 0.6f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 28) = ("gl_sircle_1610_o0v", 0.625f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 29) = ("gl_sircle_1510bx", 0.666f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 30) = ("gl_sircle_1710_o0p", 0.71f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 31) = ("gl_sircle_2316_o0p", 0.733f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 32) = ("gl_sircle_2015bx", 0.75f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 33) = ("gl_sircle_1109w", 0.82f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 34) = ("gl_sircle_1715w", 0.886f);
global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(ref buffer, 35) = ("gl_sircle_2018w", 0.9f);
OmenDonutHoleSizes = ImmutableList.Create<(string, float)>(global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan<_003C_003Ey__InlineArray36<(string, float)>, (string, float)>(in buffer, 36));
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit, Size = 128)]
internal struct _200F_206B_200F_200E_202C_202B_206D_206C_200D_206E_206E_202D_206E_206D_206A_202A_202E_200C_200C_200B_200B_202C_206F_206E_202D_200B_202D_206D_202E_200B_202D_200F_206A_202A_206D_202A_202C_200F_200C_202E
{
}

1611
FatePaths/-Module-.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>FatePaths</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<None Remove="Questionable.FatePaths.FateDefinitionSchema" />
<None Remove="Questionable.FatePaths.FateBundle" />
<EmbeddedResource Include="Questionable.FatePaths.FateDefinitionSchema" LogicalName="Questionable.FatePaths.FateDefinitionSchema" />
<EmbeddedResource Include="Questionable.FatePaths.FateBundle" LogicalName="Questionable.FatePaths.FateBundle" />
</ItemGroup>
<ItemGroup>
<Reference Include="Questionable.Model">
<HintPath>..\..\Questionable.Model.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

Binary file not shown.

View file

@ -0,0 +1,181 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/FatePaths/fatedefinition-v1.json",
"title": "FATE Definition V1",
"description": "A FATE farming definition",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"const": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/FatePaths/fatedefinition-v1.json"
},
"Name": {
"description": "Display name of the FATE",
"type": "string"
},
"Description": {
"description": "Description of the FATE activity",
"type": "string"
},
"TerritoryId": {
"description": "Territory ID where the FATE takes place",
"type": "integer",
"minimum": 1
},
"Position": {
"description": "Position to navigate to for the FATE",
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
},
"Targets": {
"description": "List of FATE targets and actions to perform on them",
"type": "array",
"items": {
"type": "object",
"properties": {
"DataId": {
"type": "integer",
"minimum": 1
},
"Action": {
"type": "string"
}
},
"required": [
"DataId",
"Action"
],
"additionalProperties": false
},
"minItems": 1
},
"RequiredQuestId": {
"description": "Quest ID that must be completed before this FATE can be farmed",
"type": "integer",
"minimum": 1
},
"RequiredStatusId": {
"description": "Status effect required to participate in the FATE",
"type": "string"
},
"TransformNpcDataId": {
"description": "NPC to interact with to obtain the required status",
"type": "integer",
"minimum": 1
},
"TransformNpcPosition": {
"description": "Position of the transform NPC",
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
},
"StopAction": {
"description": "Action to use when stopping FATE farming (e.g. to remove a transformation debuff). Only used if the player has RequiredStatusId.",
"type": "string"
},
"EventExpiry": {
"description": "If this is a seasonal/event FATE, the date and time (in UTC) when it is no longer available. Date-only values are treated as ending at daily reset (14:59:59 UTC).",
"format": "date-time",
"type": [
"string",
"null"
]
},
"TransformDialogueChoices": {
"description": "Dialogue choices when interacting with the transform NPC",
"type": "array",
"items": {
"type": "object",
"properties": {
"Type": {
"type": "string",
"enum": [
"YesNo",
"List"
]
},
"ExcelSheet": {
"type": "string"
}
},
"required": [
"Type"
],
"allOf": [
{
"if": {
"properties": {
"Type": {
"const": "YesNo"
}
}
},
"then": {
"properties": {
"Prompt": {
"type": [
"string",
"integer"
]
},
"PromptIsRegularExpression": {
"type": "boolean"
},
"Yes": {
"type": "boolean",
"default": true
}
},
"required": [
"Prompt",
"Yes"
]
}
},
{
"if": {
"properties": {
"Type": {
"const": "List"
}
}
},
"then": {
"properties": {
"Prompt": {
"type": [
"string",
"integer",
"null"
]
},
"PromptIsRegularExpression": {
"type": "boolean"
},
"Answer": {
"type": [
"string",
"integer"
]
},
"AnswerIsRegularExpression": {
"type": "boolean"
}
},
"required": [
"Prompt",
"Answer"
]
}
}
]
}
}
},
"required": [
"$schema",
"Name",
"Description",
"TerritoryId",
"Position",
"Targets"
],
"additionalProperties": false
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Threading;
using Questionable.Model.IO;
using Questionable.Model.Questing;
namespace Questionable.FatePaths;
internal static class AssemblyFateDefinitionLoader
{
private static readonly Lazy<IReadOnlyDictionary<ushort, FateDefinition>> Definitions = new Lazy<IReadOnlyDictionary<ushort, FateDefinition>>(LoadDefinitions, LazyThreadSafetyMode.ExecutionAndPublication);
internal static TimeSpan? DecodeDuration { get; private set; }
internal static Stream FateDefinitionSchema => typeof(AssemblyFateDefinitionLoader).Assembly.GetManifestResourceStream("Questionable.FatePaths.FateDefinitionSchema");
internal static IReadOnlyDictionary<ushort, FateDefinition> GetDefinitions()
{
return Definitions.Value;
}
private static IReadOnlyDictionary<ushort, FateDefinition> LoadDefinitions()
{
Stopwatch stopwatch = Stopwatch.StartNew();
using Stream stream = typeof(AssemblyFateDefinitionLoader).Assembly.GetManifestResourceStream("Questionable.FatePaths.FateBundle");
byte[] array = PathBundleSecret.Key();
List<KeyValuePair<string, FateDefinition>> list = PathBundle.Deserialize<FateDefinition>(stream, array);
CryptographicOperations.ZeroMemory(array);
Dictionary<ushort, FateDefinition> dictionary = new Dictionary<ushort, FateDefinition>(list.Count);
foreach (var (s, value) in list)
{
dictionary[ushort.Parse(s, CultureInfo.InvariantCulture)] = value;
}
DecodeDuration = stopwatch.Elapsed;
return dictionary;
}
}

View file

@ -0,0 +1,30 @@
namespace Questionable.FatePaths;
internal static class PathBundleSecret
{
private static readonly byte[] A = new byte[32]
{
48, 212, 251, 149, 233, 68, 48, 175, 219, 236,
217, 100, 130, 200, 255, 152, 233, 112, 177, 94,
93, 198, 133, 46, 110, 244, 154, 244, 247, 13,
145, 158
};
private static readonly byte[] B = new byte[32]
{
79, 60, 140, 255, 167, 84, 206, 132, 105, 228,
71, 209, 251, 6, 250, 62, 38, 86, 249, 66,
102, 234, 109, 58, 57, 229, 131, 131, 42, 66,
172, 97
};
internal static byte[] Key()
{
byte[] array = new byte[A.Length];
for (int i = 0; i < array.Length; i++)
{
array[i] = (byte)(A[i] ^ B[i]);
}
return array;
}
}

File diff suppressed because it is too large Load diff

1611
GatheringPaths/-Module-.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
<PropertyGroup>
<AssemblyName>GatheringPaths</AssemblyName>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<TargetFramework>netcoreapp9.0</TargetFramework>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
@ -14,7 +14,9 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Questionable.GatheringPaths.GatheringLocationSchema" />
<None Remove="Questionable.GatheringPaths.GatheringBundle" />
<EmbeddedResource Include="Questionable.GatheringPaths.GatheringLocationSchema" LogicalName="Questionable.GatheringPaths.GatheringLocationSchema" />
<EmbeddedResource Include="Questionable.GatheringPaths.GatheringBundle" LogicalName="Questionable.GatheringPaths.GatheringBundle" />
</ItemGroup>
<ItemGroup>
<Reference Include="Questionable.Model">

View file

@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/GatheringPaths/gatheringlocation-v1.json",
"title": "Gathering Location V1",
"description": "A series of gathering locationsk",
"description": "A series of gathering locations",
"type": "object",
"properties": {
"$schema": {

View file

@ -0,0 +1,30 @@
namespace Questionable.GatheringPaths;
internal static class PathBundleSecret
{
private static readonly byte[] A = new byte[32]
{
83, 53, 162, 177, 116, 210, 154, 147, 32, 246,
109, 49, 21, 10, 185, 40, 3, 223, 158, 234,
82, 124, 250, 209, 185, 174, 77, 93, 58, 15,
16, 246
};
private static readonly byte[] B = new byte[32]
{
11, 0, 215, 132, 153, 74, 202, 77, 15, 86,
243, 43, 187, 26, 168, 53, 42, 2, 79, 60,
154, 215, 216, 52, 147, 70, 38, 49, 232, 46,
225, 130
};
internal static byte[] Key()
{
byte[] array = new byte[A.Length];
for (int i = 0; i < array.Length; i++)
{
array[i] = (byte)(A[i] ^ B[i]);
}
return array;
}
}

File diff suppressed because it is too large Load diff

145
LLib/--z__ReadOnlyArray.cs Normal file
View file

@ -0,0 +1,145 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal sealed class _003C_003Ez__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
int ICollection.Count => _items.Length;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => this;
object? IList.this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotSupportedException();
}
}
bool IList.IsFixedSize => true;
bool IList.IsReadOnly => true;
int IReadOnlyCollection<T>.Count => _items.Length;
T IReadOnlyList<T>.this[int index] => _items[index];
int ICollection<T>.Count => _items.Length;
bool ICollection<T>.IsReadOnly => true;
T IList<T>.this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotSupportedException();
}
}
public _003C_003Ez__ReadOnlyArray(T[] items)
{
_items = items;
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_items).GetEnumerator();
}
void ICollection.CopyTo(Array array, int index)
{
((ICollection)_items).CopyTo(array, index);
}
int IList.Add(object? value)
{
throw new NotSupportedException();
}
void IList.Clear()
{
throw new NotSupportedException();
}
bool IList.Contains(object? value)
{
return ((IList)_items).Contains(value);
}
int IList.IndexOf(object? value)
{
return ((IList)_items).IndexOf(value);
}
void IList.Insert(int index, object? value)
{
throw new NotSupportedException();
}
void IList.Remove(object? value)
{
throw new NotSupportedException();
}
void IList.RemoveAt(int index)
{
throw new NotSupportedException();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)_items).GetEnumerator();
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException();
}
void ICollection<T>.Clear()
{
throw new NotSupportedException();
}
bool ICollection<T>.Contains(T item)
{
return ((ICollection<T>)_items).Contains(item);
}
void ICollection<T>.CopyTo(T[] array, int arrayIndex)
{
((ICollection<T>)_items).CopyTo(array, arrayIndex);
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
int IList<T>.IndexOf(T item)
{
return ((IList<T>)_items).IndexOf(item);
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
}

1718
LLib/-Module-.cs Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,105 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
namespace LLib.Buddy;
public static class BuddyActions
{
public const uint GysahlGreensItemId = 4868u;
private const uint SummonCompanionActionId = 24u;
private const uint DismissCompanionActionId = 25u;
public unsafe static bool TrySummonCompanion()
{
ActionManager* ptr = ActionManager.Instance();
if (ptr == null)
{
return false;
}
return ptr->UseAction(ActionType.GeneralAction, 24u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
}
public unsafe static bool TryDismissCompanion()
{
ActionManager* ptr = ActionManager.Instance();
if (ptr == null)
{
return false;
}
return ptr->UseAction(ActionType.GeneralAction, 25u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
}
public static bool TrySetOrder(byte order)
{
if (!BuddyStateHelper.IsCompanionSummoned())
{
return false;
}
GameMain.ExecuteCommand(500, order);
return true;
}
public unsafe static bool IsCompanionUnlocked()
{
UIState* ptr = UIState.Instance();
if (ptr != null)
{
return ptr->IsUnlockLinkUnlocked(8u);
}
return false;
}
public unsafe static bool CanSummonCompanion(ICondition condition)
{
if (!IsCompanionUnlocked())
{
return false;
}
if (BuddyStateHelper.IsCompanionSummoned())
{
return false;
}
if (condition[ConditionFlag.BoundByDuty] || condition[ConditionFlag.Mounted] || condition[ConditionFlag.InCombat])
{
return false;
}
InventoryManager* ptr = InventoryManager.Instance();
if (ptr == null || ptr->GetInventoryItemCount(4868u, isHq: false, checkEquipped: true, checkArmory: true, 0) == 0)
{
return false;
}
return true;
}
public unsafe static bool IsFeedAddonReady(IGameGui gameGui)
{
AtkUnitBase* addonPtr;
return gameGui.TryGetReadyAddon<AtkUnitBase>("BuddyFeed", out addonPtr);
}
public unsafe static bool TrySelectFeedItem(IGameGui gameGui, int index)
{
if (!gameGui.TryGetReadyAddon<AtkUnitBase>("BuddyFeed", out var addonPtr))
{
return false;
}
AddonCallback.Fire(addonPtr, 0, index);
return true;
}
public unsafe static bool TryConfirmFeed(IGameGui gameGui)
{
if (!gameGui.TryGetReadyAddon<AtkUnitBase>("SelectYesno", out var addonPtr))
{
return false;
}
AddonCallback.ClickYes(addonPtr);
return true;
}
}

View file

@ -0,0 +1,68 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Component.GUI;
using LLib.GameUI;
namespace LLib.Buddy;
public static class BuddyEquipActions
{
private const uint CompanionWindowActionId = 26u;
public unsafe static bool TryOpenBuddyAddon()
{
ActionManager* ptr = ActionManager.Instance();
if (ptr == null)
{
return false;
}
return ptr->UseAction(ActionType.GeneralAction, 26u, 3758096384uL, 0u, ActionManager.UseActionMode.None, 0u, null);
}
public unsafe static bool IsBuddyAddonReady(IGameGui gameGui)
{
AtkUnitBase* addonPtr;
return gameGui.TryGetReadyAddon<AtkUnitBase>("Buddy", out addonPtr);
}
public unsafe static bool TrySelectBardingSlot(IGameGui gameGui, int slot)
{
if (!gameGui.TryGetReadyAddon<AtkUnitBase>("Buddy", out var addonPtr))
{
return false;
}
AddonCallback.Fire(addonPtr, 0, 2);
AddonCallback.Fire(addonPtr, 1, slot);
return true;
}
public unsafe static bool TryEquipBarding(IGameGui gameGui, int bardingIndex)
{
if (!gameGui.TryGetReadyAddon<AtkUnitBase>("Buddy", out var addonPtr))
{
return false;
}
AddonCallback.Fire(addonPtr, 2, bardingIndex);
return true;
}
public unsafe static bool TryUnequipBarding(IGameGui gameGui, int slot)
{
if (!gameGui.TryGetReadyAddon<AtkUnitBase>("Buddy", out var addonPtr))
{
return false;
}
AddonCallback.Fire(addonPtr, 3, slot);
return true;
}
public unsafe static bool TryCloseBuddyAddon(IGameGui gameGui)
{
if (!gameGui.TryGetAddonByName<AtkUnitBase>("Buddy", out var addonPtr))
{
return false;
}
AddonCallback.Fire(addonPtr, -1);
return true;
}
}

View file

@ -0,0 +1,220 @@
using System;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
namespace LLib.Buddy;
public static class BuddyStateHelper
{
public unsafe static bool IsCompanionSummoned()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return false;
}
return ptr->Buddy.CompanionInfo.TimeLeft > 0f;
}
public unsafe static bool IsCompanionMounted()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return false;
}
return ptr->Buddy.CompanionInfo.Mounted;
}
public unsafe static string GetCompanionName()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return string.Empty;
}
return ptr->Buddy.CompanionInfo.NameString;
}
public unsafe static byte GetCompanionColorStainId()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.CurrentColorStainId;
}
public unsafe static byte GetCompanionRank()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.Rank;
}
public unsafe static uint GetCompanionCurrentXP()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0u;
}
return ptr->Buddy.CompanionInfo.CurrentXP;
}
public unsafe static byte GetCompanionStars()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.Stars;
}
public unsafe static byte GetCompanionSkillPoints()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.SkillPoints;
}
public unsafe static byte GetDefenderLevel()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.Levels[0];
}
public unsafe static byte GetAttackerLevel()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.Levels[1];
}
public unsafe static byte GetHealerLevel()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.Levels[2];
}
public unsafe static (byte Head, byte Chest, byte Feet) GetEquippedBardingIds()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return (Head: 0, Chest: 0, Feet: 0);
}
Span<byte> buddyEquipRowIds = ptr->Buddy.CompanionInfo.BuddyEquipRowIds;
return (Head: buddyEquipRowIds[0], Chest: buddyEquipRowIds[1], Feet: buddyEquipRowIds[2]);
}
public unsafe static bool IsBardingUnlocked(uint bardingId)
{
UIState* ptr = UIState.Instance();
if (ptr != null)
{
return ptr->Buddy.CompanionInfo.IsBuddyEquipUnlocked(bardingId);
}
return false;
}
public unsafe static byte GetActiveCommand()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.ActiveCommand;
}
public unsafe static byte GetFavoriteFeed()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.CompanionInfo.FavoriteFeed;
}
public unsafe static bool IsPetSummoned()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return false;
}
if (ptr->Buddy.PetInfo.Pet != null)
{
return ptr->Buddy.PetInfo.Pet->EntityId != 0;
}
return false;
}
public unsafe static byte GetPetOrder()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.PetInfo.Order;
}
public unsafe static byte GetPetStance()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return 0;
}
return ptr->Buddy.PetInfo.Stance;
}
public unsafe static bool IsBattleBuddyPresent()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return false;
}
Span<FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember> battleBuddies = ptr->Buddy.BattleBuddies;
for (int i = 0; i < battleBuddies.Length; i++)
{
if (battleBuddies[i].EntityId != 0)
{
return true;
}
}
return false;
}
public unsafe static bool HasDutyHelpers()
{
UIState* ptr = UIState.Instance();
if (ptr == null)
{
return false;
}
return ptr->Buddy.DutyHelperInfo.HasHelpers;
}
}

View file

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Microsoft.Extensions.Logging;
namespace LLib.External;
public sealed class PluginFeaturePauser : IDisposable
{
private readonly ICallGateSubscriber<string, bool?> _getEnabled;
private readonly ICallGateSubscriber<string, bool, object?> _setEnabled;
private readonly ILogger _logger;
private HashSet<string>? _pausedFeatures;
public bool IsPaused => _pausedFeatures != null;
public PluginFeaturePauser(IDalamudPluginInterface pluginInterface, string getEnabledEndpoint, string setEnabledEndpoint, ILogger logger)
{
ArgumentNullException.ThrowIfNull(pluginInterface, "pluginInterface");
ArgumentException.ThrowIfNullOrEmpty(getEnabledEndpoint, "getEnabledEndpoint");
ArgumentException.ThrowIfNullOrEmpty(setEnabledEndpoint, "setEnabledEndpoint");
ArgumentNullException.ThrowIfNull(logger, "logger");
_getEnabled = pluginInterface.GetIpcSubscriber<string, bool?>(getEnabledEndpoint);
_setEnabled = pluginInterface.GetIpcSubscriber<string, bool, object>(setEnabledEndpoint);
_logger = logger;
}
public void Disable(IEnumerable<string> features)
{
if (_pausedFeatures == null)
{
_pausedFeatures = new HashSet<string>();
}
foreach (string feature in features)
{
if (_pausedFeatures.Contains(feature))
{
continue;
}
try
{
if (_getEnabled.InvokeFunc(feature) == true)
{
_setEnabled.InvokeAction(feature, arg2: false);
_pausedFeatures.Add(feature);
_logger.LogInformation("Paused external feature: {Feature}", feature);
}
}
catch (Exception ex)
{
if (!(ex is IpcError))
{
_logger.LogWarning(ex, "Failed to pause external feature {Feature}", feature);
}
}
}
}
public void Restore()
{
if (_pausedFeatures == null)
{
return;
}
HashSet<string> hashSet = new HashSet<string>();
foreach (string pausedFeature in _pausedFeatures)
{
try
{
_setEnabled.InvokeAction(pausedFeature, arg2: true);
_logger.LogInformation("Restored external feature: {Feature}", pausedFeature);
}
catch (Exception exception)
{
hashSet.Add(pausedFeature);
_logger.LogWarning(exception, "Failed to restore external feature {Feature}, keeping it for retry", pausedFeature);
}
}
_pausedFeatures = ((hashSet.Count == 0) ? null : hashSet);
}
public void Dispose()
{
Restore();
}
}

View file

@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
namespace LLib.Fishing;
public static class WaterSurfaceDetector
{
public static bool IsFishable(Vector3 position, float rotation)
{
Vector3? hitPoint;
return CheckFishableAtRotation(position, rotation, out hitPoint);
}
public static (float Rotation, Vector3 HitPoint)? FindFishableRotation(Vector3 position, int searchSteps = 36)
{
float num = (float)Math.PI * 2f / (float)searchSteps;
List<int> list = new List<int>();
for (int i = 0; i < searchSteps; i++)
{
float rotation = (float)i * num;
if (CheckFishableAtRotation(position, rotation, out var _))
{
list.Add(i);
}
}
if (list.Count == 0)
{
return null;
}
List<int> list2 = (from g in FindContiguousGroups(list, searchSteps)
orderby g.Count descending
select g).First();
float num2 = ((float)list2[0] + (float)(list2.Count - 1) / 2f) % (float)searchSteps * num;
if (CheckFishableAtRotation(position, num2, out var hitPoint2) && hitPoint2.HasValue)
{
return (num2, hitPoint2.Value);
}
return null;
}
private unsafe static bool CheckFishableAtRotation(Vector3 position, float rotation, out Vector3? hitPoint)
{
hitPoint = null;
Framework* ptr = Framework.Instance();
if (ptr == null || ptr->BGCollisionModule == null)
{
return false;
}
BGCollisionModule* bGCollisionModule = ptr->BGCollisionModule;
float num = MathF.Cos(rotation);
float num2 = MathF.Sin(rotation);
Matrix4x4 matrix = new Matrix4x4
{
M11 = num,
M13 = 0f - num2,
M22 = 1f,
M31 = num2,
M33 = num
};
Vector3 vector = Vector3.Transform(new Vector3(0f, 0f, 2f), matrix) + position;
vector.Y += 2f;
Vector3 vector2 = new Vector3(position.X, position.Y + 0.87f, position.Z);
Vector3 vector3 = Vector3.Normalize(vector - vector2);
float maxDistance = Vector3.Distance(vector2, vector);
byte* intPtr = stackalloc byte[16];
ReadOnlySpan<int> readOnlySpan = new int[4] { 16384, 0, 16384, 0 };
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(intPtr, ref readOnlySpan[0], 16);
int* flags = (int*)intPtr;
RaycastHit raycastHit = default(RaycastHit);
if (bGCollisionModule->RaycastMaterialFilter(&raycastHit, &vector2, &vector3, maxDistance, 1, flags))
{
return false;
}
Vector3 vector4 = Vector3.Transform(new Vector3(0f, -80f, 40f), matrix);
float num3 = vector4.Length();
Vector3 vector5 = vector4 / num3;
byte* intPtr2 = stackalloc byte[16];
readOnlySpan = new int[4] { 32768, 0, 32768, 0 };
// IL cpblk instruction
Unsafe.CopyBlockUnaligned(intPtr2, ref readOnlySpan[0], 16);
int* flags2 = (int*)intPtr2;
RaycastHit raycastHit2 = default(RaycastHit);
if (bGCollisionModule->RaycastMaterialFilter(&raycastHit2, &vector, &vector5, num3, 1, flags2))
{
hitPoint = raycastHit2.Point;
return true;
}
return false;
}
private static List<List<int>> FindContiguousGroups(List<int> angles, int maxSteps)
{
if (angles.Count == 0)
{
return new List<List<int>>();
}
List<int> list = angles.OrderBy((int a) => a).ToList();
List<List<int>> list2 = new List<List<int>>();
List<int> list3 = new List<int> { list[0] };
for (int num = 1; num < list.Count; num++)
{
if (list[num] - list[num - 1] == 1)
{
list3.Add(list[num]);
continue;
}
list2.Add(list3);
int num2 = 1;
List<int> list4 = new List<int>(num2);
CollectionsMarshal.SetCount(list4, num2);
CollectionsMarshal.AsSpan(list4)[0] = list[num];
list3 = list4;
}
list2.Add(list3);
if (list2.Count > 1 && list[0] == 0)
{
if (list[list.Count - 1] == maxSteps - 1)
{
List<int> item = list2[list2.Count - 1].Concat(list2[0]).ToList();
list2.RemoveAt(list2.Count - 1);
list2.RemoveAt(0);
list2.Add(item);
}
}
return list2;
}
}

View file

@ -0,0 +1,28 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using Lumina.Excel.Sheets;
namespace LLib.GameData;
public static class ClassJobHelper
{
public static bool IsClassJobUnlocked(EClassJob classJob, IDataManager dataManager)
{
ClassJob row = dataManager.GetExcelSheet<ClassJob>().GetRow((uint)classJob);
ushort num = (ushort)row.UnlockQuest.RowId;
if (num != 0)
{
return QuestManager.IsQuestComplete(num);
}
if (row.ExpArrayIndex < 0)
{
return false;
}
return PlayerStateHelper.GetClassJobLevel(row.ExpArrayIndex) > 0;
}
public static bool IsJobUnlocked(EClassJob classJob, IDataManager dataManager)
{
return IsClassJobUnlocked((EClassJob)dataManager.GetExcelSheet<ClassJob>().GetRow((uint)classJob).ClassJobParent.RowId, dataManager);
}
}

View file

@ -0,0 +1,35 @@
namespace LLib.GameData;
public enum CommandFlag
{
DrawOrSheatheWeapon = 1,
AutoAttack = 2,
Target = 3,
Dismount = 101,
CancelCast = 105,
StatusOff = 104,
WithdrawFashionAccessory = 109,
Revive = 200,
Teleport = 202,
InstantReturn = 214,
Inspect = 300,
ChangeTitle = 302,
RepairItem = 434,
RepairEquippedItems = 435,
ExtractMateria = 437,
GearsetChange = 441,
RequestSaddleBag = 444,
BuddyOrder = 500,
InterruptEmote = 502,
EnterSwim = 608,
LeaveSwim = 609,
EnterFlight = 616,
Craft = 700,
Fish = 701,
ExitCraft = 711,
AbandonQuest = 800,
LeaveDuty = 819,
RepairEquippedItemsNPC = 1602,
Desynthesize = 2800,
ApplyGlamourPlate = 2357
}

View file

@ -0,0 +1,84 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
namespace LLib.GameData;
public static class ConditionHelper
{
public static bool IsBoundByDuty(ICondition condition)
{
if (!condition[ConditionFlag.BoundByDuty] && !condition[ConditionFlag.BoundByDuty56])
{
return condition[ConditionFlag.BoundByDuty95];
}
return true;
}
public static bool IsInCombat(ICondition condition)
{
return condition[ConditionFlag.InCombat];
}
public static bool IsInCutscene(ICondition condition)
{
if (!condition[ConditionFlag.OccupiedInCutSceneEvent] && !condition[ConditionFlag.WatchingCutscene])
{
return condition[ConditionFlag.WatchingCutscene78];
}
return true;
}
public static bool IsBetweenAreas(ICondition condition)
{
if (!condition[ConditionFlag.BetweenAreas])
{
return condition[ConditionFlag.BetweenAreas51];
}
return true;
}
public static bool IsCrafting(ICondition condition)
{
if (!condition[ConditionFlag.Crafting] && !condition[ConditionFlag.PreparingToCraft])
{
return condition[ConditionFlag.ExecutingCraftingAction];
}
return true;
}
public static bool IsGathering(ICondition condition)
{
if (!condition[ConditionFlag.Gathering])
{
return condition[ConditionFlag.ExecutingGatheringAction];
}
return true;
}
public static bool IsOccupied(ICondition condition)
{
if (!condition[ConditionFlag.Occupied] && !condition[ConditionFlag.Occupied30] && !condition[ConditionFlag.Occupied33] && !condition[ConditionFlag.Occupied38])
{
return condition[ConditionFlag.Occupied39];
}
return true;
}
public static bool IsLockedIn(ICondition condition)
{
if (!IsBoundByDuty(condition) && !IsInCutscene(condition) && !IsBetweenAreas(condition) && !IsOccupied(condition) && !condition[ConditionFlag.Unconscious] && !condition[ConditionFlag.Jumping])
{
return condition[ConditionFlag.Jumping61];
}
return true;
}
public static bool IsUnavailable(ICondition condition)
{
if (!IsLockedIn(condition) && !IsCrafting(condition) && !IsGathering(condition) && !condition[ConditionFlag.Mounted] && !condition[ConditionFlag.Mounting] && !condition[ConditionFlag.Mounting71] && !condition[ConditionFlag.ChocoboRacing] && !condition[ConditionFlag.PlayingLordOfVerminion] && !condition[ConditionFlag.Performing] && !condition[ConditionFlag.UsingFashionAccessory])
{
return condition[ConditionFlag.InFlight];
}
return true;
}
}

View file

@ -44,5 +44,6 @@ public enum EClassJob : uint
Reaper,
Sage,
Viper,
Pictomancer
Pictomancer,
Beastmaster
}

Some files were not shown because too many files have changed in this diff Show more