muffin v7.5.5
This commit is contained in:
parent
a8f2c1df37
commit
6266f9e6b7
110 changed files with 14907 additions and 6073 deletions
8
Auspex/--y__InlineArray36.cs
Normal file
8
Auspex/--y__InlineArray36.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
[InlineArray(36)]
|
||||
internal struct _003C_003Ey__InlineArray36<T>
|
||||
{
|
||||
}
|
||||
0
Auspex/-PrivateImplementationDetails-.cs
Normal file
0
Auspex/-PrivateImplementationDetails-.cs
Normal file
26
Auspex/Auspex.Lighting.Internal/GameLightData.cs
Normal file
26
Auspex/Auspex.Lighting.Internal/GameLightData.cs
Normal 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;
|
||||
}
|
||||
13
Auspex/Auspex.Lighting.Internal/GameLightVTable.cs
Normal file
13
Auspex/Auspex.Lighting.Internal/GameLightVTable.cs
Normal 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;
|
||||
}
|
||||
56
Auspex/Auspex.Lighting.Internal/LightNativeBridge.cs
Normal file
56
Auspex/Auspex.Lighting.Internal/LightNativeBridge.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
47
Auspex/Auspex.Lighting.Internal/LightRenderData.cs
Normal file
47
Auspex/Auspex.Lighting.Internal/LightRenderData.cs
Normal 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;
|
||||
}
|
||||
17
Auspex/Auspex.Lighting.Internal/LightTransform.cs
Normal file
17
Auspex/Auspex.Lighting.Internal/LightTransform.cs
Normal 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;
|
||||
}
|
||||
205
Auspex/Auspex.Lighting.Internal/NativeLightHandle.cs
Normal file
205
Auspex/Auspex.Lighting.Internal/NativeLightHandle.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Auspex/Auspex.Rendering.Direct3D/ClipZoneShader.cs
Normal file
174
Auspex/Auspex.Rendering.Direct3D/ClipZoneShader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
20
Auspex/Auspex.Rendering.Direct3D/ColorConversions.cs
Normal file
20
Auspex/Auspex.Rendering.Direct3D/ColorConversions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
787
Auspex/Auspex.Rendering.Direct3D/Direct3DRenderer.cs
Normal file
787
Auspex/Auspex.Rendering.Direct3D/Direct3DRenderer.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
288
Auspex/Auspex.Rendering.Direct3D/FanFillShader.cs
Normal file
288
Auspex/Auspex.Rendering.Direct3D/FanFillShader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
49
Auspex/Auspex.Rendering.Direct3D/FrameRenderContext.cs
Normal file
49
Auspex/Auspex.Rendering.Direct3D/FrameRenderContext.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
293
Auspex/Auspex.Rendering.Direct3D/FrameRenderTarget.cs
Normal file
293
Auspex/Auspex.Rendering.Direct3D/FrameRenderTarget.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Auspex/Auspex.Rendering.Direct3D/FullscreenPassShader.cs
Normal file
91
Auspex/Auspex.Rendering.Direct3D/FullscreenPassShader.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
147
Auspex/Auspex.Rendering.Direct3D/GpuBuffer.cs
Normal file
147
Auspex/Auspex.Rendering.Direct3D/GpuBuffer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
146
Auspex/Auspex.Rendering.Direct3D/HighResFontAtlas.cs
Normal file
146
Auspex/Auspex.Rendering.Direct3D/HighResFontAtlas.cs
Normal 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);
|
||||
}
|
||||
24
Auspex/Auspex.Rendering.Direct3D/HlslShaderBuilder.cs
Normal file
24
Auspex/Auspex.Rendering.Direct3D/HlslShaderBuilder.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
29
Auspex/Auspex.Rendering.Direct3D/HlslShaderData.cs
Normal file
29
Auspex/Auspex.Rendering.Direct3D/HlslShaderData.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
264
Auspex/Auspex.Rendering.Direct3D/QuadBlitter.cs
Normal file
264
Auspex/Auspex.Rendering.Direct3D/QuadBlitter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
98
Auspex/Auspex.Rendering.Direct3D/SceneDepthCapture.cs
Normal file
98
Auspex/Auspex.Rendering.Direct3D/SceneDepthCapture.cs
Normal 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;
|
||||
_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, ©SRV));
|
||||
_copySRV = copySRV;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Auspex/Auspex.Rendering.Direct3D/ShapeSharedShader.cs
Normal file
6
Auspex/Auspex.Rendering.Direct3D/ShapeSharedShader.cs
Normal 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}";
|
||||
}
|
||||
503
Auspex/Auspex.Rendering.Direct3D/StrokeShader.cs
Normal file
503
Auspex/Auspex.Rendering.Direct3D/StrokeShader.cs
Normal file
File diff suppressed because one or more lines are too long
225
Auspex/Auspex.Rendering.Direct3D/TriangleFillShader.cs
Normal file
225
Auspex/Auspex.Rendering.Direct3D/TriangleFillShader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
64
Auspex/Auspex.Rendering.ImGuiFallback/FallbackClipMath.cs
Normal file
64
Auspex/Auspex.Rendering.ImGuiFallback/FallbackClipMath.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Auspex/Auspex.Vfx.Internal/ActorBoundVfx.cs
Normal file
64
Auspex/Auspex.Vfx.Internal/ActorBoundVfx.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Auspex/Auspex.Vfx.Internal/FrameLifetimeTracker.cs
Normal file
87
Auspex/Auspex.Vfx.Internal/FrameLifetimeTracker.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
26
Auspex/Auspex.Vfx.Internal/PinnedAlignedMatrix.cs
Normal file
26
Auspex/Auspex.Vfx.Internal/PinnedAlignedMatrix.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
150
Auspex/Auspex.Vfx.Internal/VfxInstance.cs
Normal file
150
Auspex/Auspex.Vfx.Internal/VfxInstance.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Auspex/Auspex.Vfx.Internal/VfxNativeBridge.cs
Normal file
68
Auspex/Auspex.Vfx.Internal/VfxNativeBridge.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
10
Auspex/Auspex.Vfx.Native/VfxData.cs
Normal file
10
Auspex/Auspex.Vfx.Native/VfxData.cs
Normal 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;
|
||||
}
|
||||
8
Auspex/Auspex.Vfx.Native/VfxInitData.cs
Normal file
8
Auspex/Auspex.Vfx.Native/VfxInitData.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Auspex.Vfx.Native;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 416)]
|
||||
public struct VfxInitData
|
||||
{
|
||||
}
|
||||
14
Auspex/Auspex.Vfx.Native/VfxResourceInstance.cs
Normal file
14
Auspex/Auspex.Vfx.Native/VfxResourceInstance.cs
Normal 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
29
Auspex/Auspex.csproj
Normal 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>
|
||||
8
Auspex/Auspex/AlphaBlendMode.cs
Normal file
8
Auspex/Auspex/AlphaBlendMode.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AlphaBlendMode
|
||||
{
|
||||
None,
|
||||
Add,
|
||||
Max
|
||||
}
|
||||
12
Auspex/Auspex/AlphaBlendModes.cs
Normal file
12
Auspex/Auspex/AlphaBlendModes.cs
Normal 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));
|
||||
}
|
||||
189
Auspex/Auspex/AuspexService.cs
Normal file
189
Auspex/Auspex/AuspexService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
33
Auspex/Auspex/AxAngularGradient.cs
Normal file
33
Auspex/Auspex/AxAngularGradient.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
182
Auspex/Auspex/AxAnimation.cs
Normal file
182
Auspex/Auspex/AxAnimation.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
3
Auspex/Auspex/AxAnimationPhase.cs
Normal file
3
Auspex/Auspex/AxAnimationPhase.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace Auspex;
|
||||
|
||||
public readonly record struct AxAnimationPhase(float Duration, AxEasing Easing = AxEasing.Linear);
|
||||
116
Auspex/Auspex/AxAnimationSequence.cs
Normal file
116
Auspex/Auspex/AxAnimationSequence.cs
Normal 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
156
Auspex/Auspex/AxColor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
22
Auspex/Auspex/AxDrawHints.cs
Normal file
22
Auspex/Auspex/AxDrawHints.cs
Normal 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
1611
Auspex/Auspex/AxDrawList.cs
Normal file
File diff suppressed because it is too large
Load diff
333
Auspex/Auspex/AxDrawListObjectExtensions.cs
Normal file
333
Auspex/Auspex/AxDrawListObjectExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
20
Auspex/Auspex/AxDxParams.cs
Normal file
20
Auspex/Auspex/AxDxParams.cs
Normal 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
27
Auspex/Auspex/AxEasing.cs
Normal 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
|
||||
}
|
||||
8
Auspex/Auspex/AxFalloffType.cs
Normal file
8
Auspex/Auspex/AxFalloffType.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AxFalloffType : uint
|
||||
{
|
||||
Linear,
|
||||
Quadratic,
|
||||
Cubic
|
||||
}
|
||||
3
Auspex/Auspex/AxGradientStop.cs
Normal file
3
Auspex/Auspex/AxGradientStop.cs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
namespace Auspex;
|
||||
|
||||
public readonly record struct AxGradientStop(float Position, uint Color);
|
||||
48
Auspex/Auspex/AxLightConfig.cs
Normal file
48
Auspex/Auspex/AxLightConfig.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
13
Auspex/Auspex/AxLightFlags.cs
Normal file
13
Auspex/Auspex/AxLightFlags.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Auspex;
|
||||
|
||||
[Flags]
|
||||
public enum AxLightFlags : uint
|
||||
{
|
||||
None = 0u,
|
||||
Reflection = 1u,
|
||||
DynamicShadow = 2u,
|
||||
CharaShadow = 4u,
|
||||
ObjectShadow = 8u
|
||||
}
|
||||
9
Auspex/Auspex/AxLightType.cs
Normal file
9
Auspex/Auspex/AxLightType.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AxLightType : uint
|
||||
{
|
||||
Directional = 1u,
|
||||
PointLight,
|
||||
SpotLight,
|
||||
AreaLight
|
||||
}
|
||||
8
Auspex/Auspex/AxLineCap.cs
Normal file
8
Auspex/Auspex/AxLineCap.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AxLineCap
|
||||
{
|
||||
Butt,
|
||||
Square,
|
||||
Round
|
||||
}
|
||||
9
Auspex/Auspex/AxLineJoin.cs
Normal file
9
Auspex/Auspex/AxLineJoin.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AxLineJoin
|
||||
{
|
||||
None,
|
||||
Miter,
|
||||
Bevel,
|
||||
Round
|
||||
}
|
||||
33
Auspex/Auspex/AxLinearGradient.cs
Normal file
33
Auspex/Auspex/AxLinearGradient.cs
Normal 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
184
Auspex/Auspex/AxMinimap.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
31
Auspex/Auspex/AxMultiStopAngularGradient.cs
Normal file
31
Auspex/Auspex/AxMultiStopAngularGradient.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
70
Auspex/Auspex/AxMultiStopLinearGradient.cs
Normal file
70
Auspex/Auspex/AxMultiStopLinearGradient.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
28
Auspex/Auspex/AxMultiStopRadialGradient.cs
Normal file
28
Auspex/Auspex/AxMultiStopRadialGradient.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
30
Auspex/Auspex/AxRadialGradient.cs
Normal file
30
Auspex/Auspex/AxRadialGradient.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
8
Auspex/Auspex/AxRepeatMode.cs
Normal file
8
Auspex/Auspex/AxRepeatMode.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Auspex;
|
||||
|
||||
public enum AxRepeatMode
|
||||
{
|
||||
None,
|
||||
Repeat,
|
||||
PingPong
|
||||
}
|
||||
10
Auspex/Auspex/AxStrokeFlags.cs
Normal file
10
Auspex/Auspex/AxStrokeFlags.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Auspex;
|
||||
|
||||
[Flags]
|
||||
public enum AxStrokeFlags
|
||||
{
|
||||
None = 0,
|
||||
Closed = 1
|
||||
}
|
||||
124
Auspex/Auspex/AxTerrainMap.cs
Normal file
124
Auspex/Auspex/AxTerrainMap.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
42
Auspex/Auspex/AxTerrainQuery.cs
Normal file
42
Auspex/Auspex/AxTerrainQuery.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
39
Auspex/Auspex/AxTexture.cs
Normal file
39
Auspex/Auspex/AxTexture.cs
Normal 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
23
Auspex/Auspex/CImGui.cs
Normal 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();
|
||||
}
|
||||
8
Auspex/Auspex/IPctGradient.cs
Normal file
8
Auspex/Auspex/IPctGradient.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Auspex;
|
||||
|
||||
public interface IPctGradient
|
||||
{
|
||||
uint ColorAt(Vector3 point);
|
||||
}
|
||||
104
Auspex/Auspex/LightRenderer.cs
Normal file
104
Auspex/Auspex/LightRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
19
Auspex/Auspex/SigScanHelper.cs
Normal file
19
Auspex/Auspex/SigScanHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
Auspex/Auspex/VfxRenderer.cs
Normal file
122
Auspex/Auspex/VfxRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
193
Auspex/Auspex/VfxRendererExtensions.cs
Normal file
193
Auspex/Auspex/VfxRendererExtensions.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -14,22 +14,22 @@
|
|||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Dalamud.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lumina">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Lumina.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\FFXIVClientStructs.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Dalamud.Bindings.ImGui">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.Bindings.ImGui.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Dalamud.Bindings.ImGui.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lumina.Excel">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\Lumina.Excel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="InteropGenerator.Runtime">
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\dev\InteropGenerator.Runtime.dll</HintPath>
|
||||
<HintPath>C:\Users\Aly\AppData\Roaming\XIVLauncher\addon\Hooks\15.0.3.2\InteropGenerator.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -116,6 +116,33 @@
|
|||
"description": "Set if pathfinding should stop closer or further away from the default stop distance",
|
||||
"minimum": 0.25
|
||||
},
|
||||
"LandingZones": {
|
||||
"description": "Convex polygon boundaries for randomized movement destinations. When present, Position becomes the interaction target and a random point within a zone is used as the movement destination.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Vertices": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/Questionable.Model/common-vector3.json"
|
||||
},
|
||||
"minItems": 3
|
||||
},
|
||||
"YTolerance": {
|
||||
"type": [
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Vertices"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"IgnoreDistanceToObject": {
|
||||
"type": "boolean",
|
||||
"description": "Most interactions with objects are checked for a Y (height) difference of 2 in-game units. If set to true, the game won't attempt to get any closer if the height difference is larger than this."
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -53,9 +53,19 @@ public sealed class VectorConverter : JsonConverter<Vector3>
|
|||
public override void Write(Utf8JsonWriter writer, Vector3 value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteNumber("X", value.X);
|
||||
writer.WriteNumber("Y", value.Y);
|
||||
writer.WriteNumber("Z", value.Z);
|
||||
writer.WriteNumber("X", RoundValue(value.X));
|
||||
writer.WriteNumber("Y", RoundValue(value.Y));
|
||||
writer.WriteNumber("Z", RoundValue(value.Z));
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
private static float RoundValue(float value)
|
||||
{
|
||||
float num = MathF.Round(value, 6);
|
||||
if (!(MathF.Abs(num) < 1E-06f))
|
||||
{
|
||||
return num;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ public sealed class ElementIdConverter : JsonConverter<ElementId>
|
|||
|
||||
public override void Write(Utf8JsonWriter writer, ElementId value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (value is QuestId)
|
||||
{
|
||||
writer.WriteNumberValue(value.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,18 @@ public sealed class ElementIdListConverter : JsonConverter<List<ElementId>>
|
|||
|
||||
public override void Write(Utf8JsonWriter writer, List<ElementId> value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
writer.WriteStartArray();
|
||||
foreach (ElementId item in value)
|
||||
{
|
||||
if (item is QuestId)
|
||||
{
|
||||
writer.WriteNumberValue(item.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteStringValue(item.ToString());
|
||||
}
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,26 @@ public sealed class QuestWorkConfigConverter : JsonConverter<QuestWorkValue>
|
|||
|
||||
public override void Write(Utf8JsonWriter writer, QuestWorkValue value, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (value != null && value.High.HasValue && value.Low.HasValue && value.Mode == EQuestWorkMode.Bitwise)
|
||||
{
|
||||
byte value2 = (byte)((value.High.Value << 4) + value.Low.Value);
|
||||
writer.WriteNumberValue(value2);
|
||||
return;
|
||||
}
|
||||
writer.WriteStartObject();
|
||||
if (value.High.HasValue)
|
||||
{
|
||||
writer.WriteNumber("High", value.High.Value);
|
||||
}
|
||||
if (value.Low.HasValue)
|
||||
{
|
||||
writer.WriteNumber("Low", value.Low.Value);
|
||||
}
|
||||
if (value.Mode != EQuestWorkMode.Bitwise)
|
||||
{
|
||||
writer.WritePropertyName("Mode");
|
||||
new QuestWorkModeConverter().Write(writer, value.Mode, options);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text.Json.Serialization;
|
||||
using Questionable.Model.Common.Converter;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class LandingZone
|
||||
{
|
||||
[JsonConverter(typeof(VectorListConverter))]
|
||||
public List<Vector3> Vertices { get; set; } = new List<Vector3>();
|
||||
|
||||
public float? YTolerance { get; set; }
|
||||
|
||||
public float CalculateYTolerance()
|
||||
{
|
||||
return YTolerance ?? 2f;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Questionable.Model.Questing;
|
||||
|
||||
public sealed class QuestSequence
|
||||
{
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public byte Sequence { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ public sealed class QuestStep
|
|||
|
||||
public float? StopDistance { get; set; }
|
||||
|
||||
public List<LandingZone>? LandingZones { get; set; }
|
||||
|
||||
public ushort TerritoryId { get; set; }
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
|
|
|
|||
164
Questionable.Model/Questionable.Model/LandingZoneMath.cs
Normal file
164
Questionable.Model/Questionable.Model/LandingZoneMath.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Model;
|
||||
|
||||
public static class LandingZoneMath
|
||||
{
|
||||
private static readonly Random Rng = new Random();
|
||||
|
||||
public static Vector3 CalculateLandingLocation(IReadOnlyList<LandingZone> zones)
|
||||
{
|
||||
if (zones.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("At least one landing zone is required.", "zones");
|
||||
}
|
||||
if (zones.Count == 1)
|
||||
{
|
||||
return RandomPointInConvexPolygon(zones[0].Vertices);
|
||||
}
|
||||
float num = 0f;
|
||||
Span<float> span = stackalloc float[zones.Count];
|
||||
for (int i = 0; i < zones.Count; i++)
|
||||
{
|
||||
span[i] = CalculatePolygonArea(zones[i].Vertices);
|
||||
num += span[i];
|
||||
}
|
||||
float num2 = (float)(Rng.NextDouble() * (double)num);
|
||||
float num3 = 0f;
|
||||
for (int j = 0; j < zones.Count; j++)
|
||||
{
|
||||
num3 += span[j];
|
||||
if (num2 <= num3)
|
||||
{
|
||||
return RandomPointInConvexPolygon(zones[j].Vertices);
|
||||
}
|
||||
}
|
||||
return RandomPointInConvexPolygon(zones[zones.Count - 1].Vertices);
|
||||
}
|
||||
|
||||
public static Vector3 RandomPointInConvexPolygon(IReadOnlyList<Vector3> vertices)
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
{
|
||||
throw new ArgumentException("Polygon must have at least 3 vertices.", "vertices");
|
||||
}
|
||||
if (vertices.Count == 3)
|
||||
{
|
||||
return RandomPointInTriangle(vertices[0], vertices[1], vertices[2]);
|
||||
}
|
||||
int num = vertices.Count - 2;
|
||||
Span<float> span = stackalloc float[num];
|
||||
float num2 = 0f;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
span[i] = TriangleAreaXZ(vertices[0], vertices[i + 1], vertices[i + 2]);
|
||||
num2 += span[i];
|
||||
}
|
||||
float num3 = (float)(Rng.NextDouble() * (double)num2);
|
||||
float num4 = 0f;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
num4 += span[j];
|
||||
if (num3 <= num4)
|
||||
{
|
||||
return RandomPointInTriangle(vertices[0], vertices[j + 1], vertices[j + 2]);
|
||||
}
|
||||
}
|
||||
return RandomPointInTriangle(vertices[0], vertices[vertices.Count - 2], vertices[vertices.Count - 1]);
|
||||
}
|
||||
|
||||
private static Vector3 RandomPointInTriangle(Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
float num = (float)Rng.NextDouble();
|
||||
float num2 = (float)Rng.NextDouble();
|
||||
if (num + num2 > 1f)
|
||||
{
|
||||
num = 1f - num;
|
||||
num2 = 1f - num2;
|
||||
}
|
||||
return a + num * (b - a) + num2 * (c - a);
|
||||
}
|
||||
|
||||
private static float TriangleAreaXZ(Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
return MathF.Abs((b.X - a.X) * (c.Z - a.Z) - (c.X - a.X) * (b.Z - a.Z)) * 0.5f;
|
||||
}
|
||||
|
||||
public static float CalculatePolygonArea(IReadOnlyList<Vector3> vertices)
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
float num = 0f;
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
{
|
||||
int index = (i + 1) % vertices.Count;
|
||||
num += vertices[i].X * vertices[index].Z;
|
||||
num -= vertices[index].X * vertices[i].Z;
|
||||
}
|
||||
return MathF.Abs(num) * 0.5f;
|
||||
}
|
||||
|
||||
public static bool IsConvex(IReadOnlyList<Vector3> vertices)
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool? flag = null;
|
||||
int count = vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 vector = vertices[i];
|
||||
Vector3 vector2 = vertices[(i + 1) % count];
|
||||
Vector3 vector3 = vertices[(i + 2) % count];
|
||||
float num = (vector2.X - vector.X) * (vector3.Z - vector2.Z) - (vector2.Z - vector.Z) * (vector3.X - vector2.X);
|
||||
if (!(MathF.Abs(num) < 1E-06f))
|
||||
{
|
||||
bool flag2 = num > 0f;
|
||||
if (!flag.HasValue)
|
||||
{
|
||||
flag = flag2;
|
||||
}
|
||||
else if (flag != flag2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag.HasValue;
|
||||
}
|
||||
|
||||
public static bool IsPointInConvexPolygon(Vector2 point, IReadOnlyList<Vector3> vertices)
|
||||
{
|
||||
if (vertices.Count < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool? flag = null;
|
||||
int count = vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 vector = vertices[i];
|
||||
Vector3 vector2 = vertices[(i + 1) % count];
|
||||
float num = (vector2.X - vector.X) * (point.Y - vector.Z) - (vector2.Z - vector.Z) * (point.X - vector.X);
|
||||
if (!(MathF.Abs(num) < 1E-06f))
|
||||
{
|
||||
bool flag2 = num > 0f;
|
||||
if (!flag.HasValue)
|
||||
{
|
||||
flag = flag2;
|
||||
}
|
||||
else if (flag != flag2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -317,6 +317,7 @@ internal sealed class InteractionUiController : IDisposable
|
|||
private unsafe int? HandleListChoice(string? actualPrompt, List<string?> answers, bool checkAllSteps)
|
||||
{
|
||||
List<DialogueChoiceInfo> list = new List<DialogueChoiceInfo>();
|
||||
List<int> list2 = new List<int>();
|
||||
QuestController.QuestProgress questProgress = _questController.SimulatedQuest ?? _questController.GatheringQuest ?? _questController.StartedQuest;
|
||||
if (questProgress != null)
|
||||
{
|
||||
|
|
@ -347,6 +348,15 @@ internal sealed class InteractionUiController : IDisposable
|
|||
{
|
||||
questStep = quest.FindSequence(questProgress.Sequence)?.FindStep(questProgress.Step);
|
||||
}
|
||||
if (questStep == null && questProgress.Step == 255)
|
||||
{
|
||||
QuestSequence questSequence2 = quest.FindSequence(questProgress.Sequence);
|
||||
if (questSequence2 != null && questSequence2.Steps.Count > 0)
|
||||
{
|
||||
List<QuestStep> steps = questSequence2.Steps;
|
||||
questStep = steps[steps.Count - 1];
|
||||
}
|
||||
}
|
||||
if (questStep == null)
|
||||
{
|
||||
_logger.LogDebug("Ignoring current quest dialogue choices, no active step");
|
||||
|
|
@ -371,10 +381,10 @@ internal sealed class InteractionUiController : IDisposable
|
|||
{
|
||||
EAetheryteLocation valueOrDefault = aetheryte.GetValueOrDefault();
|
||||
int num = 1;
|
||||
List<EAetheryteLocation> list2 = new List<EAetheryteLocation>(num);
|
||||
CollectionsMarshal.SetCount(list2, num);
|
||||
CollectionsMarshal.AsSpan(list2)[0] = valueOrDefault;
|
||||
source = list2;
|
||||
List<EAetheryteLocation> list3 = new List<EAetheryteLocation>(num);
|
||||
CollectionsMarshal.SetCount(list3, num);
|
||||
CollectionsMarshal.AsSpan(list3)[0] = valueOrDefault;
|
||||
source = list3;
|
||||
}
|
||||
}
|
||||
flag = questStep.InteractionType == EInteractionType.UnlockTaxiStand;
|
||||
|
|
@ -451,30 +461,30 @@ internal sealed class InteractionUiController : IDisposable
|
|||
{
|
||||
continue;
|
||||
}
|
||||
List<DialogueChoice> list3 = knownQuest.FindSequence(0)?.Steps.SelectMany((QuestStep x) => x.DialogueChoices).ToList();
|
||||
if (list3 != null && list3.Count > 0)
|
||||
List<DialogueChoice> list4 = knownQuest.FindSequence(0)?.Steps.SelectMany((QuestStep x) => x.DialogueChoices).ToList();
|
||||
if (list4 != null && list4.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}", list3.Count, item.Name);
|
||||
list.AddRange(list3.Select((DialogueChoice x) => new DialogueChoiceInfo(knownQuest, x)));
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from not accepted quest {QuestName}", list4.Count, item.Name);
|
||||
list.AddRange(list4.Select((DialogueChoice x) => new DialogueChoiceInfo(knownQuest, x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_fateController.IsRunning)
|
||||
{
|
||||
List<DialogueChoice> list4 = _fateController.CurrentFate?.TransformDialogueChoices;
|
||||
if (list4 != null)
|
||||
List<DialogueChoice> list5 = _fateController.CurrentFate?.TransformDialogueChoices;
|
||||
if (list5 != null)
|
||||
{
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from active FATE", list4.Count);
|
||||
list.AddRange(list4.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from active FATE", list5.Count);
|
||||
list.AddRange(list5.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
|
||||
}
|
||||
}
|
||||
if (_seasonalDutyController.IsRunning)
|
||||
{
|
||||
List<DialogueChoice> list5 = _seasonalDutyController.CurrentDuty?.DialogueChoices;
|
||||
if (list5 != null && list5.Count > 0)
|
||||
List<DialogueChoice> list6 = _seasonalDutyController.CurrentDuty?.DialogueChoices;
|
||||
if (list6 != null && list6.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from active seasonal duty", list5.Count);
|
||||
list.AddRange(list5.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
|
||||
_logger.LogInformation("Adding {Count} dialogue choices from active seasonal duty", list6.Count);
|
||||
list.AddRange(list6.Select((DialogueChoice x) => new DialogueChoiceInfo(null, x)));
|
||||
}
|
||||
}
|
||||
if (list.Count == 0)
|
||||
|
|
@ -522,19 +532,24 @@ internal sealed class InteractionUiController : IDisposable
|
|||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}", stringOrRegex);
|
||||
continue;
|
||||
}
|
||||
if (actualPrompt != null && (stringOrRegex == null || !IsMatch(actualPrompt, stringOrRegex)))
|
||||
if (actualPrompt != null && stringOrRegex != null && !IsMatch(actualPrompt, stringOrRegex))
|
||||
{
|
||||
_logger.LogInformation("Unexpected excelPrompt: {ExcelPrompt}, actualPrompt: {ActualPrompt}", stringOrRegex, actualPrompt);
|
||||
int? num3 = TryFindAnswerMatchIndex(answers, stringOrRegex2, dialogueChoice2.AnswerIsRegularExpression, quest3, dialogueChoice2);
|
||||
if (num3.HasValue)
|
||||
{
|
||||
list2.Add(num3.Value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (dialogueChoice2.AnswerIsRegularExpression && stringOrRegex2 != null)
|
||||
{
|
||||
int? num3 = FindBestRegexMatch(answers, stringOrRegex2, quest3, dialogueChoice2);
|
||||
if (!num3.HasValue)
|
||||
int? num4 = FindBestRegexMatch(answers, stringOrRegex2, quest3, dialogueChoice2);
|
||||
if (!num4.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}' (best regex match)", num3.Value, answers[num3.Value], actualPrompt);
|
||||
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}' (best regex match)", num4.Value, answers[num4.Value], actualPrompt);
|
||||
if (quest3?.Id is SatisfactionSupplyNpcId)
|
||||
{
|
||||
if (_questController.GatheringQuest == null || _questController.GatheringQuest.Sequence == byte.MaxValue)
|
||||
|
|
@ -544,16 +559,16 @@ internal sealed class InteractionUiController : IDisposable
|
|||
_questController.GatheringQuest.SetSequence(1);
|
||||
_questController.StartGatheringQuest("SatisfactionSupply turn in");
|
||||
}
|
||||
return num3.Value;
|
||||
return num4.Value;
|
||||
}
|
||||
for (int num4 = 0; num4 < answers.Count; num4++)
|
||||
for (int num5 = 0; num5 < answers.Count; num5++)
|
||||
{
|
||||
_logger.LogTrace("Checking if {ActualAnswer} == {ExpectedAnswer}", answers[num4], stringOrRegex2);
|
||||
if (!IsMatch(answers[num4], stringOrRegex2))
|
||||
_logger.LogTrace("Checking if {ActualAnswer} == {ExpectedAnswer}", answers[num5], stringOrRegex2);
|
||||
if (!IsMatch(answers[num5], stringOrRegex2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}'", num4, answers[num4], actualPrompt);
|
||||
_logger.LogInformation("Returning {Index}: '{Answer}' for '{Prompt}'", num5, answers[num5], actualPrompt);
|
||||
if (quest3?.Id is SatisfactionSupplyNpcId)
|
||||
{
|
||||
if (_questController.GatheringQuest == null || _questController.GatheringQuest.Sequence == byte.MaxValue)
|
||||
|
|
@ -563,13 +578,39 @@ internal sealed class InteractionUiController : IDisposable
|
|||
_questController.GatheringQuest.SetSequence(1);
|
||||
_questController.StartGatheringQuest("SatisfactionSupply turn in");
|
||||
}
|
||||
return num4;
|
||||
return num5;
|
||||
}
|
||||
}
|
||||
int[] array = list2.Distinct().ToArray();
|
||||
if (array.Length == 1)
|
||||
{
|
||||
_logger.LogWarning("Using answer-only fallback for prompt '{Prompt}'. Selected index {Index}: '{Answer}'", actualPrompt, array[0], answers[array[0]]);
|
||||
return array[0];
|
||||
}
|
||||
_logger.LogInformation("No matching answer found for {Prompt}.", actualPrompt);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int? TryFindAnswerMatchIndex(List<string?> answers, StringOrRegex? excelAnswer, bool answerIsRegularExpression, Questionable.Model.Quest? quest, DialogueChoice dialogueChoice)
|
||||
{
|
||||
if (excelAnswer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (answerIsRegularExpression)
|
||||
{
|
||||
return FindBestRegexMatch(answers, excelAnswer, quest, dialogueChoice);
|
||||
}
|
||||
for (int i = 0; i < answers.Count; i++)
|
||||
{
|
||||
if (IsMatch(answers[i], excelAnswer))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int? FindBestRegexMatch(List<string?> answers, StringOrRegex expectedAnswer, Questionable.Model.Quest? quest, DialogueChoice dialogueChoice)
|
||||
{
|
||||
List<int> list = (from x in answers.Select((string answer, int index) => new { answer, index })
|
||||
|
|
@ -731,6 +772,15 @@ internal sealed class InteractionUiController : IDisposable
|
|||
else
|
||||
{
|
||||
QuestStep questStep = quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step);
|
||||
if (questStep == null && currentQuest.Step == 255)
|
||||
{
|
||||
QuestSequence questSequence2 = quest.FindSequence(currentQuest.Sequence);
|
||||
if (questSequence2 != null && questSequence2.Steps.Count > 0)
|
||||
{
|
||||
List<QuestStep> steps = questSequence2.Steps;
|
||||
questStep = steps[steps.Count - 1];
|
||||
}
|
||||
}
|
||||
if (questStep != null && HandleDefaultYesNo(addonSelectYesno, quest, questStep, questStep.DialogueChoices, actualPrompt))
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ internal static class Duty
|
|||
if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions))
|
||||
{
|
||||
AutoDutyIpc.DutyMode dutyMode = GetDutyMode(step.DutyOptions.ContentFinderConditionId, step.DutyOptions.DutyMode);
|
||||
if (dutyMode == AutoDutyIpc.DutyMode.UnsyncRegular && (step.DutyOptions.DutyMode == EDutyMode.UnsyncParty || (!step.DutyOptions.DutyMode.HasValue && configuration.Duties.DutyModeOverrides.TryGetValue(step.DutyOptions.ContentFinderConditionId, out var value) && value == EDutyMode.UnsyncParty) || (!step.DutyOptions.DutyMode.HasValue && !configuration.Duties.DutyModeOverrides.ContainsKey(step.DutyOptions.ContentFinderConditionId) && configuration.Duties.DefaultDutyMode == EDutyMode.UnsyncParty)))
|
||||
if (dutyMode == AutoDutyIpc.DutyMode.UnsyncRegular && (step.DutyOptions.DutyMode == EDutyMode.UnsyncParty || (!step.DutyOptions.DutyMode.HasValue && configuration.Duties.DutyModeOverrides.TryGetValue(step.DutyOptions.ContentFinderConditionId, out var value) && value == Configuration.EDutyMode.UnsyncParty) || (!step.DutyOptions.DutyMode.HasValue && !configuration.Duties.DutyModeOverrides.ContainsKey(step.DutyOptions.ContentFinderConditionId) && configuration.Duties.DefaultDutyMode == Configuration.EDutyMode.UnsyncParty)))
|
||||
{
|
||||
yield return new WaitForPartyTask();
|
||||
}
|
||||
|
|
@ -71,6 +71,17 @@ internal static class Duty
|
|||
_ => AutoDutyIpc.DutyMode.Support,
|
||||
};
|
||||
}
|
||||
|
||||
private static AutoDutyIpc.DutyMode ConvertToAutoDutyMode(Configuration.EDutyMode mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
Configuration.EDutyMode.Support => AutoDutyIpc.DutyMode.Support,
|
||||
Configuration.EDutyMode.UnsyncSolo => AutoDutyIpc.DutyMode.UnsyncRegular,
|
||||
Configuration.EDutyMode.UnsyncParty => AutoDutyIpc.DutyMode.UnsyncRegular,
|
||||
_ => AutoDutyIpc.DutyMode.Support,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record WaitForPartyTask : ITask
|
||||
|
|
|
|||
|
|
@ -56,7 +56,10 @@ internal static class Interact
|
|||
{
|
||||
yield break;
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(step.DataId, "step.DataId");
|
||||
if (!step.DataId.HasValue)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
if (sequence.Sequence == 0 && sequence.Steps.IndexOf(step) == 0)
|
||||
{
|
||||
yield return new WaitAtEnd.WaitDelay();
|
||||
|
|
@ -72,22 +75,21 @@ internal static class Interact
|
|||
SkipStepConditions stepIf = skipConditions.StepIf;
|
||||
if (stepIf != null && stepIf.Never)
|
||||
{
|
||||
goto IL_025f;
|
||||
goto IL_025d;
|
||||
}
|
||||
}
|
||||
if (step.InteractionType != EInteractionType.PurchaseItem)
|
||||
{
|
||||
skipMarkerCheck = ((step.DataId == 1052475) ? 1 : 0);
|
||||
goto IL_0260;
|
||||
goto IL_025e;
|
||||
}
|
||||
}
|
||||
goto IL_025f;
|
||||
IL_0260:
|
||||
yield return new Task(value, quest, interactionType2, (byte)skipMarkerCheck != 0, step.PickUpItemId ?? step.GCPurchase?.ItemId, step.TaxiStandId, step.SkipConditions?.StepIf, step.CompletionQuestVariablesFlags);
|
||||
yield break;
|
||||
IL_025f:
|
||||
goto IL_025d;
|
||||
IL_025d:
|
||||
skipMarkerCheck = 1;
|
||||
goto IL_0260;
|
||||
goto IL_025e;
|
||||
IL_025e:
|
||||
yield return new Task(value, quest, interactionType2, (byte)skipMarkerCheck != 0, step.PickUpItemId ?? step.GCPurchase?.ItemId, step.TaxiStandId, step.SkipConditions?.StepIf, step.CompletionQuestVariablesFlags);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,20 +230,11 @@ internal static class Interact
|
|||
}
|
||||
_needsUnmount = false;
|
||||
}
|
||||
EStatus? completionStatusId = base.Task.CompletionStatusId;
|
||||
if (completionStatusId.HasValue)
|
||||
{
|
||||
EStatus valueOrDefault = completionStatusId.GetValueOrDefault();
|
||||
if (gameFunctions.HasStatus(valueOrDefault))
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
}
|
||||
uint? pickUpItemId = base.Task.PickUpItemId;
|
||||
if (pickUpItemId.HasValue)
|
||||
{
|
||||
uint valueOrDefault2 = pickUpItemId.GetValueOrDefault();
|
||||
if (InventoryManager.Instance()->GetInventoryItemCount(valueOrDefault2, isHq: false, checkEquipped: true, checkArmory: true, 0) > 0)
|
||||
uint valueOrDefault = pickUpItemId.GetValueOrDefault();
|
||||
if (InventoryManager.Instance()->GetInventoryItemCount(valueOrDefault, isHq: false, checkEquipped: true, checkArmory: true, 0) > 0)
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
|
@ -251,8 +244,8 @@ internal static class Interact
|
|||
byte? taxiStandId = base.Task.TaxiStandId;
|
||||
if (taxiStandId.HasValue)
|
||||
{
|
||||
byte valueOrDefault3 = taxiStandId.GetValueOrDefault();
|
||||
if (UIState.Instance()->IsChocoboTaxiStandUnlocked((uint)(valueOrDefault3 + 1179648)))
|
||||
byte valueOrDefault2 = taxiStandId.GetValueOrDefault();
|
||||
if (UIState.Instance()->IsChocoboTaxiStandUnlocked((uint)(valueOrDefault2 + 1179648)))
|
||||
{
|
||||
return ETaskResult.TaskComplete;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ internal static class MoveTo
|
|||
}
|
||||
if (step.Position.HasValue)
|
||||
{
|
||||
return CreateMoveTasks(step, step.Position.Value);
|
||||
List<LandingZone> landingZones = step.LandingZones;
|
||||
Vector3 destination = ((landingZones == null || landingZones.Count <= 0) ? step.Position.Value : LandingZoneMath.CalculateLandingLocation(step.LandingZones));
|
||||
return CreateMoveTasks(step, destination);
|
||||
}
|
||||
if (step != null && step.DataId.HasValue && step.StopDistance.HasValue)
|
||||
{
|
||||
|
|
@ -46,13 +48,13 @@ internal static class MoveTo
|
|||
{
|
||||
valueOrDefault = aetheryte.GetValueOrDefault();
|
||||
flag = true;
|
||||
goto IL_00e2;
|
||||
goto IL_0108;
|
||||
}
|
||||
}
|
||||
}
|
||||
flag = false;
|
||||
goto IL_00e2;
|
||||
IL_00e2:
|
||||
goto IL_0108;
|
||||
IL_0108:
|
||||
if (flag)
|
||||
{
|
||||
return CreateMoveTasks(step, aetheryteData.Locations[valueOrDefault]);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
|||
using LLib.GameData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Data;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Controller.Utils;
|
||||
|
||||
|
|
@ -145,9 +144,9 @@ internal sealed class PartyWatchdog : IDisposable
|
|||
}
|
||||
if (_configuration.Duties.DutyModeOverrides.TryGetValue(cfcId, out var value))
|
||||
{
|
||||
return value == EDutyMode.UnsyncParty;
|
||||
return value == Configuration.EDutyMode.UnsyncParty;
|
||||
}
|
||||
return _configuration.Duties.DefaultDutyMode == EDutyMode.UnsyncParty;
|
||||
return _configuration.Duties.DefaultDutyMode == Configuration.EDutyMode.UnsyncParty;
|
||||
}
|
||||
|
||||
private void StopIfRunning(string reason)
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ internal sealed class CommandHandler : IDisposable
|
|||
_chatGui.Print("/qst help-all - displays all available commands", "Questionable", 576);
|
||||
_chatGui.Print("/qst config - opens the configuration window", "Questionable", 576);
|
||||
_chatGui.Print("/qst changelog - opens the changelog window", "Questionable", 576);
|
||||
_chatGui.Print("/qst changelog - opens the changelog window", "Questionable", 576);
|
||||
_chatGui.Print("/qst start - starts doing quests", "Questionable", 576);
|
||||
_chatGui.Print("/qst stop - stops doing quests", "Questionable", 576);
|
||||
_chatGui.Print("/qst reload - reload all quest data", "Questionable", 576);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ namespace Questionable.Controller;
|
|||
|
||||
internal sealed class QuestRegistry
|
||||
{
|
||||
internal sealed record FailedQuestLoad(ElementId QuestId, string? FilePath, string ErrorMessage, Quest.ESource Source);
|
||||
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
|
||||
private readonly QuestData _questData;
|
||||
|
|
@ -49,8 +51,12 @@ internal sealed class QuestRegistry
|
|||
|
||||
private readonly Dictionary<ElementId, string> _questFolderNames = new Dictionary<ElementId, string>();
|
||||
|
||||
private readonly Dictionary<ElementId, FailedQuestLoad> _failedLoads = new Dictionary<ElementId, FailedQuestLoad>();
|
||||
|
||||
public IEnumerable<Quest> AllQuests => _quests.Values;
|
||||
|
||||
public IReadOnlyDictionary<ElementId, FailedQuestLoad> FailedLoads => _failedLoads;
|
||||
|
||||
public int Count => _quests.Count<KeyValuePair<ElementId, Quest>>((KeyValuePair<ElementId, Quest> x) => !x.Value.Root.Disabled);
|
||||
|
||||
public int ValidationIssueCount => _questValidator.IssueCount;
|
||||
|
|
@ -77,6 +83,7 @@ internal sealed class QuestRegistry
|
|||
{
|
||||
_questValidator.Reset();
|
||||
_quests.Clear();
|
||||
_failedLoads.Clear();
|
||||
_contentFinderConditionIds.Clear();
|
||||
_lowPriorityContentFinderConditionQuests.Clear();
|
||||
_questFolderNames.Clear();
|
||||
|
|
@ -244,7 +251,7 @@ internal sealed class QuestRegistry
|
|||
_questValidator.Validate(_quests.Values.Where((Quest x) => x.Source != Quest.ESource.Assembly).ToList());
|
||||
}
|
||||
|
||||
private void LoadQuestFromStream(string fileName, Stream stream, Quest.ESource source, string directoryName)
|
||||
private void LoadQuestFromStream(string fileName, Stream stream, Quest.ESource source, string directoryName, string? filePath = null)
|
||||
{
|
||||
if (source == Quest.ESource.UserDirectory)
|
||||
{
|
||||
|
|
@ -259,9 +266,26 @@ internal sealed class QuestRegistry
|
|||
try
|
||||
{
|
||||
jsonNode = JsonNode.Parse(stream);
|
||||
string text = FindDuplicateKey(jsonNode);
|
||||
if (text != null)
|
||||
{
|
||||
string errorMessage = "Duplicate JSON property '" + text + "'. Remove the duplicate key.";
|
||||
_questValidator.AddValidationIssue(new ValidationIssue
|
||||
{
|
||||
ElementId = elementId,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSyntax,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Duplicate JSON property '{text}' in file '{fileName}'. Remove the duplicate key."
|
||||
});
|
||||
_failedLoads[elementId] = new FailedQuestLoad(elementId, filePath, errorMessage, source);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
string errorMessage2 = "JSON syntax error: " + ex.Message;
|
||||
ValidationIssue issue = new ValidationIssue
|
||||
{
|
||||
ElementId = elementId,
|
||||
|
|
@ -269,9 +293,10 @@ internal sealed class QuestRegistry
|
|||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSyntax,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"JSON parsing error in file '{fileName}': {ex.Message}\n\nThis usually indicates a syntax error such as:\n\ufffd Missing comma between properties\n\ufffd Unclosed quotes or brackets\n\ufffd Invalid escape sequences\n\ufffd Trailing commas where not allowed\n\nPlease check the JSON syntax around the indicated position."
|
||||
Description = $"JSON parsing error in file '{fileName}': {ex.Message}\n\nThis usually indicates a syntax error such as:\n■ Missing comma between properties\n■ Unclosed quotes or brackets\n■ Invalid escape sequences\n■ Trailing commas where not allowed\n\nPlease check the JSON syntax around the indicated position."
|
||||
};
|
||||
_questValidator.AddValidationIssue(issue);
|
||||
_failedLoads[elementId] = new FailedQuestLoad(elementId, filePath, errorMessage2, source);
|
||||
return;
|
||||
}
|
||||
_jsonSchemaValidator.Enqueue(elementId, jsonNode);
|
||||
|
|
@ -312,7 +337,26 @@ internal sealed class QuestRegistry
|
|||
}
|
||||
}
|
||||
}
|
||||
QuestRoot root = jsonNode.Deserialize<QuestRoot>();
|
||||
QuestRoot root;
|
||||
try
|
||||
{
|
||||
root = jsonNode.Deserialize<QuestRoot>();
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
string errorMessage3 = "Failed to deserialize quest data: " + ex2.Message;
|
||||
_questValidator.AddValidationIssue(new ValidationIssue
|
||||
{
|
||||
ElementId = elementId,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSchema,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Failed to deserialize '" + fileName + "': " + ex2.Message
|
||||
});
|
||||
_failedLoads[elementId] = new FailedQuestLoad(elementId, filePath, errorMessage3, source);
|
||||
return;
|
||||
}
|
||||
if (!_questData.TryGetQuestInfo(elementId, out IQuestInfo questInfo))
|
||||
{
|
||||
if (!(elementId is UnlockLinkId unlockLinkId))
|
||||
|
|
@ -323,9 +367,9 @@ internal sealed class QuestRegistry
|
|||
string name;
|
||||
try
|
||||
{
|
||||
string text = fileName.Substring(0, fileName.Length - ".json".Length);
|
||||
int num = text.IndexOf('_', StringComparison.Ordinal);
|
||||
name = ((num >= 0 && num + 1 < text.Length) ? text.Substring(num + 1) : text);
|
||||
string text2 = fileName.Substring(0, fileName.Length - ".json".Length);
|
||||
int num = text2.IndexOf('_', StringComparison.Ordinal);
|
||||
name = ((num >= 0 && num + 1 < text2.Length) ? text2.Substring(num + 1) : text2);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -398,11 +442,17 @@ internal sealed class QuestRegistry
|
|||
try
|
||||
{
|
||||
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
||||
LoadQuestFromStream(fileInfo.Name, stream, source, directory.Name);
|
||||
LoadQuestFromStream(fileInfo.Name, stream, source, directory.Name, fileInfo.FullName);
|
||||
}
|
||||
catch (Exception innerException)
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidDataException("Unable to load file " + fileInfo.FullName, innerException);
|
||||
ElementId elementId = ExtractQuestIdFromName(fileInfo.Name);
|
||||
string errorMessage = "Unexpected error loading file: " + ex.Message;
|
||||
_logger.LogError(ex, "Failed to load quest from '{FileName}', skipping", fileInfo.FullName);
|
||||
if (elementId != null)
|
||||
{
|
||||
_failedLoads[elementId] = new FailedQuestLoad(elementId, fileInfo.FullName, errorMessage, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
DirectoryInfo[] directories = directory.GetDirectories();
|
||||
|
|
@ -412,6 +462,46 @@ internal sealed class QuestRegistry
|
|||
}
|
||||
}
|
||||
|
||||
private static string? FindDuplicateKey(JsonNode? node)
|
||||
{
|
||||
if (node is JsonObject jsonObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var (text2, node2) in jsonObject)
|
||||
{
|
||||
if (!hashSet.Add(text2))
|
||||
{
|
||||
return text2;
|
||||
}
|
||||
string text3 = FindDuplicateKey(node2);
|
||||
if (text3 != null)
|
||||
{
|
||||
return text3;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.Message.Contains("same key", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Match match = Regex.Match(ex.Message, "Key: (.+)");
|
||||
return match.Success ? match.Groups[1].Value.Trim() : "unknown";
|
||||
}
|
||||
}
|
||||
else if (node is JsonArray jsonArray)
|
||||
{
|
||||
foreach (JsonNode item in jsonArray)
|
||||
{
|
||||
string text4 = FindDuplicateKey(item);
|
||||
if (text4 != null)
|
||||
{
|
||||
return text4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ElementId? ExtractQuestIdFromName(string resourceName)
|
||||
{
|
||||
string text = resourceName.Substring(0, resourceName.Length - ".json".Length);
|
||||
|
|
@ -428,6 +518,11 @@ internal sealed class QuestRegistry
|
|||
return _quests.ContainsKey(questId);
|
||||
}
|
||||
|
||||
public bool IsFailedLoad(ElementId questId)
|
||||
{
|
||||
return _failedLoads.ContainsKey(questId);
|
||||
}
|
||||
|
||||
public bool TryGetQuest(ElementId questId, [NotNullWhen(true)] out Quest? quest)
|
||||
{
|
||||
return _quests.TryGetValue(questId, out quest);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -700,11 +700,11 @@ internal sealed class QuestionableIpc : IDisposable
|
|||
|
||||
private bool SetDefaultDutyMode(int dutyMode)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(EDutyMode), dutyMode))
|
||||
if (!Enum.IsDefined(typeof(Configuration.EDutyMode), dutyMode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_configuration.Duties.DefaultDutyMode = (EDutyMode)dutyMode;
|
||||
_configuration.Duties.DefaultDutyMode = (Configuration.EDutyMode)dutyMode;
|
||||
_pluginInterface.SavePluginConfig(_configuration);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -720,11 +720,11 @@ internal sealed class QuestionableIpc : IDisposable
|
|||
|
||||
private bool SetDutyModeOverride(uint contentFinderConditionId, int dutyMode)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(EDutyMode), dutyMode))
|
||||
if (!Enum.IsDefined(typeof(Configuration.EDutyMode), dutyMode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_configuration.Duties.DutyModeOverrides[contentFinderConditionId] = (EDutyMode)dutyMode;
|
||||
_configuration.Duties.DutyModeOverrides[contentFinderConditionId] = (Configuration.EDutyMode)dutyMode;
|
||||
_pluginInterface.SavePluginConfig(_configuration);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,13 +123,17 @@ internal sealed class GameFunctions
|
|||
|
||||
public IGameObject? FindObjectByDataId(uint dataId, Dalamud.Game.ClientState.Objects.Enums.ObjectKind? kind = null, bool warnIfMissing = true)
|
||||
{
|
||||
foreach (IGameObject item in _objectTable)
|
||||
for (int i = 0; i < _objectTable.Length; i++)
|
||||
{
|
||||
Dalamud.Game.ClientState.Objects.Enums.ObjectKind objectKind = item.ObjectKind;
|
||||
bool flag = ((objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Pc || objectKind - 8 <= Dalamud.Game.ClientState.Objects.Enums.ObjectKind.BattleNpc || objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.HousingEventObject) ? true : false);
|
||||
if (!flag && (item == null || item.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.GatheringPoint || item.IsTargetable) && item.BaseId == dataId && (!kind.HasValue || kind.Value == item.ObjectKind))
|
||||
IGameObject gameObject = _objectTable[i];
|
||||
if (gameObject != null)
|
||||
{
|
||||
return item;
|
||||
Dalamud.Game.ClientState.Objects.Enums.ObjectKind objectKind = gameObject.ObjectKind;
|
||||
bool flag = ((objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Pc || objectKind - 8 <= Dalamud.Game.ClientState.Objects.Enums.ObjectKind.BattleNpc || objectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.HousingEventObject) ? true : false);
|
||||
if (!flag && (gameObject == null || gameObject.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.GatheringPoint || gameObject.IsTargetable) && gameObject.BaseId == dataId && (!kind.HasValue || kind.Value == gameObject.ObjectKind))
|
||||
{
|
||||
return gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (warnIfMissing)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ internal sealed class Quest
|
|||
|
||||
public QuestSequence? FindSequence(byte currentSequence)
|
||||
{
|
||||
return Root.QuestSequence.SingleOrDefault((QuestSequence seq) => seq.Sequence == currentSequence);
|
||||
return Root.QuestSequence.FirstOrDefault((QuestSequence seq) => seq.Sequence == currentSequence);
|
||||
}
|
||||
|
||||
public IEnumerable<QuestSequence> AllSequences()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using System.Text.RegularExpressions;
|
||||
using Json.Schema;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
|
@ -15,8 +20,21 @@ namespace Questionable.Validation.Validators;
|
|||
|
||||
internal sealed class JsonSchemaValidator : IQuestValidator
|
||||
{
|
||||
private const string QuestSchemaUri = "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/QuestPaths/quest-v1.json";
|
||||
|
||||
private readonly Dictionary<ElementId, JsonNode> _questNodes = new Dictionary<ElementId, JsonNode>();
|
||||
|
||||
private static readonly JsonSerializerOptions ValidationSerializationOptions = new JsonSerializerOptions
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
|
||||
IncludeFields = false,
|
||||
WriteIndented = false,
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver
|
||||
{
|
||||
Modifiers = { (Action<JsonTypeInfo>)NoEmptyCollectionModifier }
|
||||
}
|
||||
};
|
||||
|
||||
private JsonSchema? _questSchema;
|
||||
|
||||
public JsonSchemaValidator()
|
||||
|
|
@ -100,58 +118,205 @@ internal sealed class JsonSchemaValidator : IQuestValidator
|
|||
{
|
||||
yield break;
|
||||
}
|
||||
EvaluationResults evaluationResults = _questSchema.Evaluate(value, new EvaluationOptions
|
||||
EvaluationResults evaluationResults = null;
|
||||
ValidationIssue validationIssue = null;
|
||||
try
|
||||
{
|
||||
Culture = CultureInfo.InvariantCulture,
|
||||
OutputFormat = OutputFormat.List
|
||||
});
|
||||
if (evaluationResults.IsValid)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
var array = (from r in GetInvalidResults(evaluationResults).ToArray()
|
||||
group r by r.InstanceLocation?.ToString() ?? "<root>").Select(delegate(IGrouping<string, EvaluationResults> g)
|
||||
{
|
||||
string[] messages = (from m in g.SelectMany((EvaluationResults r) => r.Errors?.Values ?? Enumerable.Empty<string>())
|
||||
where !string.IsNullOrWhiteSpace(m)
|
||||
select m.Trim()).Distinct().ToArray();
|
||||
return new
|
||||
evaluationResults = _questSchema.Evaluate(value, new EvaluationOptions
|
||||
{
|
||||
Path = g.Key,
|
||||
Messages = messages
|
||||
};
|
||||
}).ToArray();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine("JSON Validation failed:");
|
||||
if (array.Length == 0)
|
||||
Culture = CultureInfo.InvariantCulture,
|
||||
OutputFormat = OutputFormat.List
|
||||
});
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.Message.Contains("same key", StringComparison.Ordinal))
|
||||
{
|
||||
stringBuilder.AppendLine(" - <unknown>: validation failed");
|
||||
Match match = Regex.Match(ex.Message, "Key: (.+)");
|
||||
string text = (match.Success ? match.Groups[1].Value.Trim() : "unknown");
|
||||
validationIssue = new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSyntax,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Duplicate JSON property '" + text + "' in quest file. Remove the duplicate key."
|
||||
};
|
||||
}
|
||||
if (validationIssue != null)
|
||||
{
|
||||
yield return validationIssue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var array2 = array;
|
||||
foreach (var anon in array2)
|
||||
if (evaluationResults == null || evaluationResults.IsValid)
|
||||
{
|
||||
string value2 = ((anon.Messages.Length != 0) ? string.Join("; ", anon.Messages) : "validation failed");
|
||||
StringBuilder stringBuilder2 = stringBuilder;
|
||||
IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
|
||||
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 2, stringBuilder2, invariantCulture);
|
||||
handler.AppendLiteral(" - ");
|
||||
handler.AppendFormatted(anon.Path);
|
||||
handler.AppendLiteral(": ");
|
||||
handler.AppendFormatted(value2);
|
||||
stringBuilder2.AppendLine(invariantCulture, ref handler);
|
||||
yield break;
|
||||
}
|
||||
var array = (from r in GetInvalidResults(evaluationResults).ToArray()
|
||||
where !(r.EvaluationPath?.ToString() ?? string.Empty).Contains("/if", StringComparison.Ordinal)
|
||||
group r by r.InstanceLocation?.ToString() ?? "<root>").Select(delegate(IGrouping<string, EvaluationResults> g)
|
||||
{
|
||||
string[] messages = (from m in g.SelectMany((EvaluationResults r) => r.Errors?.Values ?? Enumerable.Empty<string>())
|
||||
where !string.IsNullOrWhiteSpace(m)
|
||||
select m.Trim()).Distinct().ToArray();
|
||||
return new
|
||||
{
|
||||
Path = g.Key,
|
||||
Messages = messages
|
||||
};
|
||||
}).ToArray();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine("JSON Validation failed:");
|
||||
if (array.Length == 0)
|
||||
{
|
||||
stringBuilder.AppendLine(" - <unknown>: validation failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
var array2 = array;
|
||||
foreach (var anon in array2)
|
||||
{
|
||||
string value2 = ((anon.Messages.Length != 0) ? string.Join("; ", anon.Messages) : "validation failed");
|
||||
StringBuilder stringBuilder2 = stringBuilder;
|
||||
IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
|
||||
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 2, stringBuilder2, invariantCulture);
|
||||
handler.AppendLiteral(" - ");
|
||||
handler.AppendFormatted(anon.Path);
|
||||
handler.AppendLiteral(": ");
|
||||
handler.AppendFormatted(value2);
|
||||
stringBuilder2.AppendLine(invariantCulture, ref handler);
|
||||
}
|
||||
}
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSchema,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = stringBuilder.ToString().TrimEnd()
|
||||
};
|
||||
}
|
||||
static IEnumerable<EvaluationResults> GetInvalidResults(EvaluationResults result)
|
||||
{
|
||||
if (!result.IsValid)
|
||||
{
|
||||
yield return result;
|
||||
}
|
||||
if (result.HasDetails)
|
||||
{
|
||||
foreach (EvaluationResults detail in result.Details)
|
||||
{
|
||||
foreach (EvaluationResults invalidResult in GetInvalidResults(detail))
|
||||
{
|
||||
yield return invalidResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yield return new ValidationIssue
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationIssue> ValidateFromQuestRoot(Quest quest)
|
||||
{
|
||||
if (!(JsonSerializer.SerializeToNode(quest.Root, ValidationSerializationOptions) is JsonObject jsonObject))
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSchema,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = stringBuilder.ToString().TrimEnd()
|
||||
};
|
||||
return Array.Empty<ValidationIssue>();
|
||||
}
|
||||
JsonObject jsonObject2 = new JsonObject { ["$schema"] = "https://github.com/WigglyMuffin/Questionable/raw/refs/heads/main/QuestPaths/quest-v1.json" };
|
||||
foreach (KeyValuePair<string, JsonNode> item in jsonObject)
|
||||
{
|
||||
item.Deconstruct(out var key, out var value);
|
||||
string propertyName = key;
|
||||
jsonObject2[propertyName] = value?.DeepClone();
|
||||
}
|
||||
return EvaluateQuestNode(quest, jsonObject2);
|
||||
}
|
||||
|
||||
private IEnumerable<ValidationIssue> EvaluateQuestNode(Quest quest, JsonNode questNode)
|
||||
{
|
||||
if (_questSchema == null)
|
||||
{
|
||||
_questSchema = JsonSchema.FromStream(AssemblyQuestLoader.QuestSchema).AsTask().Result;
|
||||
}
|
||||
EvaluationResults evaluationResults = null;
|
||||
ValidationIssue validationIssue = null;
|
||||
try
|
||||
{
|
||||
evaluationResults = _questSchema.Evaluate(questNode, new EvaluationOptions
|
||||
{
|
||||
Culture = CultureInfo.InvariantCulture,
|
||||
OutputFormat = OutputFormat.List
|
||||
});
|
||||
}
|
||||
catch (ArgumentException ex) when (ex.Message.Contains("same key", StringComparison.Ordinal))
|
||||
{
|
||||
Match match = Regex.Match(ex.Message, "Key: (.+)");
|
||||
string text = (match.Success ? match.Groups[1].Value.Trim() : "unknown");
|
||||
validationIssue = new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSyntax,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = "Duplicate JSON property '" + text + "' in quest file. Remove the duplicate key."
|
||||
};
|
||||
}
|
||||
if (validationIssue != null)
|
||||
{
|
||||
yield return validationIssue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (evaluationResults == null || evaluationResults.IsValid)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
var array = (from r in GetInvalidResults(evaluationResults).ToArray()
|
||||
where !(r.EvaluationPath?.ToString() ?? string.Empty).Contains("/if", StringComparison.Ordinal)
|
||||
group r by r.InstanceLocation?.ToString() ?? "<root>").Select(delegate(IGrouping<string, EvaluationResults> g)
|
||||
{
|
||||
string[] messages = (from m in g.SelectMany((EvaluationResults r) => r.Errors?.Values ?? Enumerable.Empty<string>())
|
||||
where !string.IsNullOrWhiteSpace(m)
|
||||
select m.Trim()).Distinct().ToArray();
|
||||
return new
|
||||
{
|
||||
Path = g.Key,
|
||||
Messages = messages
|
||||
};
|
||||
}).ToArray();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.AppendLine("JSON Validation failed:");
|
||||
if (array.Length == 0)
|
||||
{
|
||||
stringBuilder.AppendLine(" - <unknown>: validation failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
var array2 = array;
|
||||
foreach (var anon in array2)
|
||||
{
|
||||
string value = ((anon.Messages.Length != 0) ? string.Join("; ", anon.Messages) : "validation failed");
|
||||
StringBuilder stringBuilder2 = stringBuilder;
|
||||
IFormatProvider invariantCulture = CultureInfo.InvariantCulture;
|
||||
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 2, stringBuilder2, invariantCulture);
|
||||
handler.AppendLiteral(" - ");
|
||||
handler.AppendFormatted(anon.Path);
|
||||
handler.AppendLiteral(": ");
|
||||
handler.AppendFormatted(value);
|
||||
stringBuilder2.AppendLine(invariantCulture, ref handler);
|
||||
}
|
||||
}
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = null,
|
||||
Step = null,
|
||||
Type = EIssueType.InvalidJsonSchema,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = stringBuilder.ToString().TrimEnd()
|
||||
};
|
||||
}
|
||||
static IEnumerable<EvaluationResults> GetInvalidResults(EvaluationResults result)
|
||||
{
|
||||
if (!result.IsValid)
|
||||
|
|
@ -180,4 +345,15 @@ internal sealed class JsonSchemaValidator : IQuestValidator
|
|||
{
|
||||
_questNodes.Clear();
|
||||
}
|
||||
|
||||
private static void NoEmptyCollectionModifier(JsonTypeInfo typeInfo)
|
||||
{
|
||||
foreach (JsonPropertyInfo property in typeInfo.Properties)
|
||||
{
|
||||
if (typeof(ICollection).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
property.ShouldSerialize = (object _, object? val) => val is ICollection collection && collection.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
|
||||
namespace Questionable.Validation.Validators;
|
||||
|
||||
internal sealed class LandingZoneValidator : IQuestValidator
|
||||
{
|
||||
public IEnumerable<ValidationIssue> Validate(Quest quest)
|
||||
{
|
||||
foreach (var stepInfo in quest.AllSteps().Where<(QuestSequence, int, QuestStep)>(delegate((QuestSequence Sequence, int StepId, QuestStep Step) x)
|
||||
{
|
||||
List<LandingZone> landingZones = x.Step.LandingZones;
|
||||
return landingZones != null && landingZones.Count > 0;
|
||||
}))
|
||||
{
|
||||
for (int z = 0; z < stepInfo.Item3.LandingZones.Count; z++)
|
||||
{
|
||||
LandingZone zone = stepInfo.Item3.LandingZones[z];
|
||||
if (zone.Vertices.Count < 3)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = stepInfo.Item1.Sequence,
|
||||
Step = stepInfo.Item2,
|
||||
Type = EIssueType.InvalidLandingZone,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Landing zone {z} has fewer than 3 vertices"
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (!LandingZoneMath.IsConvex(zone.Vertices))
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = stepInfo.Item1.Sequence,
|
||||
Step = stepInfo.Item2,
|
||||
Type = EIssueType.InvalidLandingZone,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Landing zone {z} is not convex"
|
||||
};
|
||||
}
|
||||
if (LandingZoneMath.CalculatePolygonArea(zone.Vertices) < 0.01f)
|
||||
{
|
||||
yield return new ValidationIssue
|
||||
{
|
||||
ElementId = quest.Id,
|
||||
Sequence = stepInfo.Item1.Sequence,
|
||||
Step = stepInfo.Item2,
|
||||
Type = EIssueType.InvalidLandingZone,
|
||||
Severity = EIssueSeverity.Error,
|
||||
Description = $"Landing zone {z} has zero area (collinear vertices)"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,5 +3,6 @@ namespace Questionable.Validation;
|
|||
internal enum EIssueSeverity
|
||||
{
|
||||
None,
|
||||
Error
|
||||
Error,
|
||||
Warning
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,5 +21,6 @@ public enum EIssueType
|
|||
ClassQuestWithoutAetheryteShortcut,
|
||||
DuplicateSinglePlayerInstance,
|
||||
UnusedSinglePlayerInstance,
|
||||
InvalidChatMessage
|
||||
InvalidChatMessage,
|
||||
InvalidLandingZone
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Questionable.Model;
|
||||
using Questionable.Model.Questing;
|
||||
using Questionable.Validation.Validators;
|
||||
|
||||
namespace Questionable.Validation;
|
||||
|
||||
|
|
@ -111,4 +112,24 @@ internal sealed class QuestValidator
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
public List<ValidationIssue> ValidateQuestSync(Quest quest)
|
||||
{
|
||||
List<ValidationIssue> list = new List<ValidationIssue>();
|
||||
foreach (IQuestValidator validator in _validators)
|
||||
{
|
||||
foreach (ValidationIssue item in (validator is JsonSchemaValidator jsonSchemaValidator) ? jsonSchemaValidator.ValidateFromQuestRoot(quest) : validator.Validate(quest))
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
return (from x in list
|
||||
orderby x.Sequence, x.Step, x.Description
|
||||
select x).ToList();
|
||||
}
|
||||
|
||||
public void ClearIssuesForQuest(ElementId questId)
|
||||
{
|
||||
_validationIssues.RemoveAll((ValidationIssue issue) => issue.ElementId == questId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,15 +62,23 @@ internal sealed class DebugConfigComponent : ConfigComponent
|
|||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("The Party Watchdog stops Questionable when entering certain zones with other party members, or when entering unsupported content. Disabling this allows Questionable to continue working while in a party, but may cause unexpected behavior in group content.");
|
||||
bool v6 = base.Configuration.Advanced.ShowWindowInInstances;
|
||||
if (ImGui.Checkbox("Show quest window in instances (ignore 'Hide in all instances' setting)", ref v6))
|
||||
{
|
||||
base.Configuration.Advanced.ShowWindowInInstances = v6;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("When enabled, the quest window will always be shown in instanced duties, ignoring the 'Hide quest window in all instanced duties' setting in General.");
|
||||
ImGui.Separator();
|
||||
ImGui.Text("AutoDuty Settings");
|
||||
using (ImRaii.PushIndent())
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
bool v6 = base.Configuration.Advanced.DisableAutoDutyBareMode;
|
||||
if (ImGui.Checkbox("Use Pre-Loop/Loop/Post-Loop settings", ref v6))
|
||||
bool v7 = base.Configuration.Advanced.DisableAutoDutyBareMode;
|
||||
if (ImGui.Checkbox("Use Pre-Loop/Loop/Post-Loop settings", ref v7))
|
||||
{
|
||||
base.Configuration.Advanced.DisableAutoDutyBareMode = v6;
|
||||
base.Configuration.Advanced.DisableAutoDutyBareMode = v7;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
|
@ -80,42 +88,42 @@ internal sealed class DebugConfigComponent : ConfigComponent
|
|||
ImGui.Text("Quest/Interaction Skips");
|
||||
using (ImRaii.PushIndent())
|
||||
{
|
||||
bool v7 = base.Configuration.Advanced.SkipAetherCurrents;
|
||||
if (ImGui.Checkbox("Don't pick up aether currents/aether current quests", ref v7))
|
||||
bool v8 = base.Configuration.Advanced.SkipAetherCurrents;
|
||||
if (ImGui.Checkbox("Don't pick up aether currents/aether current quests", ref v8))
|
||||
{
|
||||
base.Configuration.Advanced.SkipAetherCurrents = v7;
|
||||
base.Configuration.Advanced.SkipAetherCurrents = v8;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("If not done during the MSQ by Questionable, you have to manually pick up any missed aether currents/quests. There is no way to automatically pick up all missing aether currents.");
|
||||
bool v8 = base.Configuration.Advanced.SkipClassJobQuests;
|
||||
if (ImGui.Checkbox("Don't pick up class/job/role quests", ref v8))
|
||||
bool v9 = base.Configuration.Advanced.SkipClassJobQuests;
|
||||
if (ImGui.Checkbox("Don't pick up class/job/role quests", ref v9))
|
||||
{
|
||||
base.Configuration.Advanced.SkipClassJobQuests = v8;
|
||||
base.Configuration.Advanced.SkipClassJobQuests = v9;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("Class and job skills for A Realm Reborn, Heavensward and (for the Lv70 skills) Stormblood are locked behind quests. Not recommended if you plan on queueing for instances with duty finder/party finder.\n\nNote: This setting is ignored for the first class/job quest if your character is not high enough level to start the level 4 MSQ.");
|
||||
bool v9 = base.Configuration.Advanced.SkipARealmRebornHardModePrimals;
|
||||
if (ImGui.Checkbox("Don't pick up ARR hard mode primal quests", ref v9))
|
||||
bool v10 = base.Configuration.Advanced.SkipARealmRebornHardModePrimals;
|
||||
if (ImGui.Checkbox("Don't pick up ARR hard mode primal quests", ref v10))
|
||||
{
|
||||
base.Configuration.Advanced.SkipARealmRebornHardModePrimals = v9;
|
||||
base.Configuration.Advanced.SkipARealmRebornHardModePrimals = v10;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("Hard mode Ifrit/Garuda/Titan are required for the Patch 2.5 quest 'Good Intentions' and to start Heavensward.");
|
||||
bool v10 = base.Configuration.Advanced.SkipCrystalTowerRaids;
|
||||
if (ImGui.Checkbox("Don't pick up Crystal Tower quests", ref v10))
|
||||
bool v11 = base.Configuration.Advanced.SkipCrystalTowerRaids;
|
||||
if (ImGui.Checkbox("Don't pick up Crystal Tower quests", ref v11))
|
||||
{
|
||||
base.Configuration.Advanced.SkipCrystalTowerRaids = v10;
|
||||
base.Configuration.Advanced.SkipCrystalTowerRaids = v11;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("Crystal Tower raids are required for the Patch 2.55 quest 'A Time to Every Purpose' and to start Heavensward.");
|
||||
bool v11 = base.Configuration.Advanced.PreventQuestCompletion;
|
||||
if (ImGui.Checkbox("Prevent quest completion", ref v11))
|
||||
bool v12 = base.Configuration.Advanced.PreventQuestCompletion;
|
||||
if (ImGui.Checkbox("Prevent quest completion", ref v12))
|
||||
{
|
||||
base.Configuration.Advanced.PreventQuestCompletion = v11;
|
||||
base.Configuration.Advanced.PreventQuestCompletion = v12;
|
||||
Save();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue