787 lines
28 KiB
C#
787 lines
28 KiB
C#
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));
|
|
}
|
|
}
|